diff --git a/src/postgres/src/test/regress/expected/yb_pg_orafce.out b/src/postgres/src/test/regress/expected/yb_pg_orafce.out new file mode 100644 index 000000000000..e3f6ea4f50c6 --- /dev/null +++ b/src/postgres/src/test/regress/expected/yb_pg_orafce.out @@ -0,0 +1,4402 @@ +\set ECHO none +-- +-- test built-in date type oracle compatibility functions +-- +SELECT add_months ('2003-08-01', 3); + add_months +------------ + 2003-11-01 +(1 row) + +SELECT add_months ('2003-08-01', -3); + add_months +------------ + 2003-05-01 +(1 row) + +SELECT add_months ('2003-08-21', -3); + add_months +------------ + 2003-05-21 +(1 row) + +SELECT add_months ('2003-01-31', 1); + add_months +------------ + 2003-02-28 +(1 row) + +SELECT add_months ('2008-02-28', 1); + add_months +------------ + 2008-03-28 +(1 row) + +SELECT add_months ('2008-02-29', 1); + add_months +------------ + 2008-03-31 +(1 row) + +SELECT add_months ('2008-01-31', 12); + add_months +------------ + 2009-01-31 +(1 row) + +SELECT add_months ('2008-01-31', -12); + add_months +------------ + 2007-01-31 +(1 row) + +SELECT add_months ('2008-01-31', 95903); + add_months +------------ + 9999-12-31 +(1 row) + +SELECT add_months ('2008-01-31', -80640); + add_months +--------------- + 4712-01-31 BC +(1 row) + +SELECT add_months ('03-21-2008',3); + add_months +------------ + 2008-06-21 +(1 row) + +SELECT add_months ('21-MAR-2008',3); + add_months +------------ + 2008-06-21 +(1 row) + +SELECT add_months ('21-MAR-08',3); + add_months +------------ + 2008-06-21 +(1 row) + +SELECT add_months ('2008-MAR-21',3); + add_months +------------ + 2008-06-21 +(1 row) + +SELECT add_months ('March 21,2008',3); + add_months +------------ + 2008-06-21 +(1 row) + +SELECT add_months('03/21/2008',3); + add_months +------------ + 2008-06-21 +(1 row) + +SELECT add_months('20080321',3); + add_months +------------ + 2008-06-21 +(1 row) + +SELECT add_months('080321',3); + add_months +------------ + 2008-06-21 +(1 row) + +SET search_path TO oracle,"$user", public, pg_catalog; +SELECT add_months ('2003-08-01 10:12:21', 3); + add_months +--------------------- + 2003-11-01 10:12:21 +(1 row) + +SELECT add_months ('2003-08-01 10:21:21', -3); + add_months +--------------------- + 2003-05-01 10:21:21 +(1 row) + +SELECT add_months ('2003-08-21 12:21:21', -3); + add_months +--------------------- + 2003-05-21 12:21:21 +(1 row) + +SELECT add_months ('2003-01-31 01:12:45', 1); + add_months +--------------------- + 2003-02-28 01:12:45 +(1 row) + +SELECT add_months ('2008-02-28 02:12:12', 1); + add_months +--------------------- + 2008-03-28 02:12:12 +(1 row) + +SELECT add_months ('2008-02-29 12:12:12', 1); + add_months +--------------------- + 2008-03-31 12:12:12 +(1 row) + +SELECT add_months ('2008-01-31 11:11:21', 12); + add_months +--------------------- + 2009-01-31 11:11:21 +(1 row) + +SELECT add_months ('2008-01-31 11:21:21', -12); + add_months +--------------------- + 2007-01-31 11:21:21 +(1 row) + +SELECT add_months ('2008-01-31 12:12:12', 95903); + add_months +--------------------- + 9999-12-31 12:12:12 +(1 row) + +SELECT add_months ('2008-01-31 11:32:12', -80640); + add_months +------------------------ + 4712-01-31 11:32:12 BC +(1 row) + +SELECT add_months ('03-21-2008 08:12:22',3); + add_months +--------------------- + 2008-06-21 08:12:22 +(1 row) + +SELECT add_months ('21-MAR-2008 06:02:12',3); + add_months +--------------------- + 2008-06-21 06:02:12 +(1 row) + +SELECT add_months ('21-MAR-08 12:11:22',3); + add_months +--------------------- + 2008-06-21 12:11:22 +(1 row) + +SELECT add_months ('2008-MAR-21 11:32:43',3); + add_months +--------------------- + 2008-06-21 11:32:43 +(1 row) + +SELECT add_months ('March 21,2008 12:32:12',3); + add_months +--------------------- + 2008-06-21 12:32:12 +(1 row) + +SELECT add_months('03/21/2008 12:32:12',3); + add_months +--------------------- + 2008-06-21 12:32:12 +(1 row) + +SELECT add_months('20080321 123244',3); + add_months +--------------------- + 2008-06-21 12:32:44 +(1 row) + +SELECT add_months('080321 121212',3); + add_months +--------------------- + 2008-06-21 12:12:12 +(1 row) + +SET search_path TO default; +SELECT last_day(to_date('2003/03/15', 'yyyy/mm/dd')); + last_day +------------ + 2003-03-31 +(1 row) + +SELECT last_day(to_date('2003/02/03', 'yyyy/mm/dd')); + last_day +------------ + 2003-02-28 +(1 row) + +SELECT last_day(to_date('2004/02/03', 'yyyy/mm/dd')); + last_day +------------ + 2004-02-29 +(1 row) + +SELECT last_day('1900-02-01'); + last_day +------------ + 1900-02-28 +(1 row) + +SELECT last_day('2000-02-01'); + last_day +------------ + 2000-02-29 +(1 row) + +SELECT last_day('2007-02-01'); + last_day +------------ + 2007-02-28 +(1 row) + +SELECT last_day('2008-02-01'); + last_day +------------ + 2008-02-29 +(1 row) + +SET search_path TO oracle,"$user", public, pg_catalog; +SELECT last_day(to_date('2003/03/15 11:12:21', 'yyyy/mm/dd hh:mi:ss')); + last_day +--------------------- + 2003-03-31 11:12:21 +(1 row) + +SELECT last_day(to_date('2003/02/03 10:21:32', 'yyyy/mm/dd hh:mi:ss')); + last_day +--------------------- + 2003-02-28 10:21:32 +(1 row) + +SELECT last_day(to_date('2004/02/03 11:32:12', 'yyyy/mm/dd hh:mi:ss')); + last_day +--------------------- + 2004-02-29 11:32:12 +(1 row) + +SELECT last_day('1900-02-01 12:12:11'); + last_day +--------------------- + 1900-02-28 12:12:11 +(1 row) + +SELECT last_day('2000-02-01 121143'); + last_day +--------------------- + 2000-02-29 12:11:43 +(1 row) + +SELECT last_day('2007-02-01 12:21:33'); + last_day +--------------------- + 2007-02-28 12:21:33 +(1 row) + +SELECT last_day('2008-02-01 121212'); + last_day +--------------------- + 2008-02-29 12:12:12 +(1 row) + +SET search_path TO default; +SELECT next_day ('2003-08-01', 'TUESDAY'); + next_day +------------ + 2003-08-05 +(1 row) + +SELECT next_day ('2003-08-06', 'WEDNESDAY'); + next_day +------------ + 2003-08-13 +(1 row) + +SELECT next_day ('2003-08-06', 'SUNDAY'); + next_day +------------ + 2003-08-10 +(1 row) + +SELECT next_day ('2008-01-01', 'sun'); + next_day +------------ + 2008-01-06 +(1 row) + +SELECT next_day ('2008-01-01', 'sunAAA'); + next_day +------------ + 2008-01-06 +(1 row) + +SELECT next_day ('2008-01-01', 1); + next_day +------------ + 2008-01-06 +(1 row) + +SELECT next_day ('2008-01-01', 7); + next_day +------------ + 2008-01-05 +(1 row) + +SET search_path TO oracle,"$user", public, pg_catalog; +SELECT next_day ('2003-08-01 111211', 'TUESDAY'); + next_day +--------------------- + 2003-08-05 11:12:11 +(1 row) + +SELECT next_day ('2003-08-06 10:11:43', 'WEDNESDAY'); + next_day +--------------------- + 2003-08-13 10:11:43 +(1 row) + +SELECT next_day ('2003-08-06 11:21:21', 'SUNDAY'); + next_day +--------------------- + 2003-08-10 11:21:21 +(1 row) + +SELECT next_day ('2008-01-01 111343', 'sun'); + next_day +--------------------- + 2008-01-06 11:13:43 +(1 row) + +SELECT next_day ('2008-01-01 121212', 'sunAAA'); + next_day +--------------------- + 2008-01-06 12:12:12 +(1 row) + +SELECT next_day ('2008-01-01 111213', 1); + next_day +--------------------- + 2008-01-06 11:12:13 +(1 row) + +SELECT next_day ('2008-01-01 11:12:13', 7); + next_day +--------------------- + 2008-01-05 11:12:13 +(1 row) + +SET search_path TO default; +SELECT months_between (to_date ('2003/01/01', 'yyyy/mm/dd'), to_date ('2003/03/14', 'yyyy/mm/dd')); + months_between +------------------- + -2.41935483870968 +(1 row) + +SELECT months_between (to_date ('2003/07/01', 'yyyy/mm/dd'), to_date ('2003/03/14', 'yyyy/mm/dd')); + months_between +------------------ + 3.58064516129032 +(1 row) + +SELECT months_between (to_date ('2003/07/02', 'yyyy/mm/dd'), to_date ('2003/07/02', 'yyyy/mm/dd')); + months_between +---------------- + 0 +(1 row) + +SELECT months_between (to_date ('2003/08/02', 'yyyy/mm/dd'), to_date ('2003/06/02', 'yyyy/mm/dd')); + months_between +---------------- + 2 +(1 row) + +SELECT months_between ('2007-02-28', '2007-04-30'); + months_between +---------------- + -2 +(1 row) + +SELECT months_between ('2008-01-31', '2008-02-29'); + months_between +---------------- + -1 +(1 row) + +SELECT months_between ('2008-02-29', '2008-03-31'); + months_between +---------------- + -1 +(1 row) + +SELECT months_between ('2008-02-29', '2008-04-30'); + months_between +---------------- + -2 +(1 row) + +SELECT trunc(months_between('21-feb-2008', '2008-02-29')); + trunc +------- + 0 +(1 row) + +SET search_path TO oracle,"$user", public, pg_catalog; +SELECT months_between (to_date ('2003/01/01 12:12:12', 'yyyy/mm/dd h24:mi:ss'), to_date ('2003/03/14 11:11:11', 'yyyy/mm/dd h24:mi:ss')); + months_between +------------------- + -2.41935483870968 +(1 row) + +SELECT months_between (to_date ('2003/07/01 10:11:11', 'yyyy/mm/dd h24:mi:ss'), to_date ('2003/03/14 10:12:12', 'yyyy/mm/dd h24:mi:ss')); + months_between +------------------ + 3.58064516129032 +(1 row) + +SELECT months_between (to_date ('2003/07/02 11:21:21', 'yyyy/mm/dd h24:mi:ss'), to_date ('2003/07/02 11:11:11', 'yyyy/mm/dd h24:mi:ss')); + months_between +---------------- + 0 +(1 row) + +SELECT months_between (to_timestamp ('2003/08/02 10:11:12', 'yyyy/mm/dd h24:mi:ss'), to_date ('2003/06/02 10:10:11', 'yyyy/mm/dd h24:mi:ss')); + months_between +---------------- + 2 +(1 row) + +SELECT months_between ('2007-02-28 111111', '2007-04-30 112121'); + months_between +---------------- + -2 +(1 row) + +SELECT months_between ('2008-01-31 11:32:11', '2008-02-29 11:12:12'); + months_between +---------------- + -1 +(1 row) + +SELECT months_between ('2008-02-29 10:11:13', '2008-03-31 10:12:11'); + months_between +---------------- + -1 +(1 row) + +SELECT months_between ('2008-02-29 111111', '2008-04-30 12:12:12'); + months_between +---------------- + -2 +(1 row) + +SELECT trunc(months_between('21-feb-2008 12:11:11', '2008-02-29 11:11:11')); + trunc +------- + 0 +(1 row) + +SET search_path TO default; +select length('jmenuji se Pavel Stehule'),dbms_pipe.pack_message('jmenuji se Pavel Stehule'); + length | pack_message +--------+-------------- + 24 | +(1 row) + +select length('a bydlim ve Skalici'),dbms_pipe.pack_message('a bydlim ve Skalici'); + length | pack_message +--------+-------------- + 19 | +(1 row) + +select dbms_pipe.send_message('pavel',0,1); + send_message +-------------- + 0 +(1 row) + +select dbms_pipe.send_message('pavel',0,2); + send_message +-------------- + 0 +(1 row) + +select dbms_pipe.receive_message('pavel',0); + receive_message +----------------- + 0 +(1 row) + +select '>>>>'||dbms_pipe.unpack_message_text()||'<<<<'; + ?column? +---------------------------------- + >>>>jmenuji se Pavel Stehule<<<< +(1 row) + +select '>>>>'||dbms_pipe.unpack_message_text()||'<<<<'; + ?column? +----------------------------- + >>>>a bydlim ve Skalici<<<< +(1 row) + +select dbms_pipe.receive_message('pavel',0); + receive_message +----------------- + 0 +(1 row) + +select dbms_pipe.purge('bob'); + purge +------- + +(1 row) + +select dbms_pipe.reset_buffer(); + reset_buffer +-------------- + +(1 row) + +select dbms_pipe.pack_message('012345678901234+1'); + pack_message +-------------- + +(1 row) + +select dbms_pipe.send_message('bob',0,10); + send_message +-------------- + 0 +(1 row) + +select dbms_pipe.pack_message('012345678901234+2'); + pack_message +-------------- + +(1 row) + +select dbms_pipe.send_message('bob',0,10); + send_message +-------------- + 0 +(1 row) + +select dbms_pipe.pack_message('012345678901234+3'); + pack_message +-------------- + +(1 row) + +select dbms_pipe.send_message('bob',0,10); + send_message +-------------- + 0 +(1 row) + +-------------------------------------------- +select dbms_pipe.receive_message('bob',0); + receive_message +----------------- + 0 +(1 row) + +select dbms_pipe.unpack_message_text(); + unpack_message_text +--------------------- + 012345678901234+1 +(1 row) + +select dbms_pipe.receive_message('bob',0); + receive_message +----------------- + 0 +(1 row) + +select dbms_pipe.unpack_message_text(); + unpack_message_text +--------------------- + 012345678901234+2 +(1 row) + +select dbms_pipe.receive_message('bob',0); + receive_message +----------------- + 0 +(1 row) + +select dbms_pipe.unpack_message_text(); + unpack_message_text +--------------------- + 012345678901234+3 +(1 row) + +select dbms_pipe.unique_session_name() LIKE 'PG$PIPE$%'; + ?column? +---------- + t +(1 row) + +select dbms_pipe.pack_message('012345678901234-1'); + pack_message +-------------- + +(1 row) + +select dbms_pipe.send_message('bob',0,10); + send_message +-------------- + 0 +(1 row) + +select dbms_pipe.receive_message('bob',0); + receive_message +----------------- + 0 +(1 row) + +select dbms_pipe.unpack_message_text(); + unpack_message_text +--------------------- + 012345678901234-1 +(1 row) + +select dbms_pipe.pack_message('012345678901234-2'); + pack_message +-------------- + +(1 row) + +select dbms_pipe.send_message('bob',0,10); + send_message +-------------- + 0 +(1 row) + +select dbms_pipe.send_message('bob',0,10); + send_message +-------------- + 0 +(1 row) + +select dbms_pipe.receive_message('bob',0); + receive_message +----------------- + 0 +(1 row) + +select dbms_pipe.unpack_message_text(); + unpack_message_text +--------------------- + 012345678901234-2 +(1 row) + +select dbms_pipe.pack_message(TO_DATE('2006-10-11', 'YYYY-MM-DD')); + pack_message +-------------- + +(1 row) + +select dbms_pipe.send_message('test_date'); + send_message +-------------- + 0 +(1 row) + +select dbms_pipe.receive_message('test_date'); + receive_message +----------------- + 0 +(1 row) + +select dbms_pipe.next_item_type(); + next_item_type +---------------- + 12 +(1 row) + +select dbms_pipe.unpack_message_date(); + unpack_message_date +--------------------- + 2006-10-11 +(1 row) + +select dbms_pipe.pack_message(to_timestamp('2008-10-30 01:23:45', 'YYYY-MM-DD HH24:MI:SS')); + pack_message +-------------- + +(1 row) + +select dbms_pipe.send_message('test_timestamp'); + send_message +-------------- + 0 +(1 row) + +select dbms_pipe.receive_message('test_timestamp'); + receive_message +----------------- + 0 +(1 row) + +select dbms_pipe.next_item_type(); + next_item_type +---------------- + 13 +(1 row) + +select to_char(dbms_pipe.unpack_message_timestamp(), 'YYYY-MM-DD HH24:MI:SS'); + to_char +--------------------- + 2008-10-30 01:23:45 +(1 row) + +select dbms_pipe.pack_message(6262626262::numeric); + pack_message +-------------- + +(1 row) + +select dbms_pipe.send_message('test_int'); + send_message +-------------- + 0 +(1 row) + +select dbms_pipe.receive_message('test_int'); + receive_message +----------------- + 0 +(1 row) + +select dbms_pipe.next_item_type(); + next_item_type +---------------- + 9 +(1 row) + +select dbms_pipe.unpack_message_number(); + unpack_message_number +----------------------- + 6262626262 +(1 row) + +select dbms_pipe.purge('bob'); + purge +------- + +(1 row) + +select name, items, "limit", private, owner from dbms_pipe.db_pipes where name = 'bob'; + name | items | limit | private | owner +------+-------+-------+---------+------- +(0 rows) + +select PLVstr.betwn('Harry and Sally are very happy', 7, 9); + betwn +------- + and +(1 row) + +select PLVstr.betwn('Harry and Sally are very happy', 7, 9, FALSE); + betwn +------- + n +(1 row) + +select PLVstr.betwn('Harry and Sally are very happy', -3, -1); + betwn +------- + ppy +(1 row) + +select PLVstr.betwn('Harry and Sally are very happy', 'a', 'ry'); + betwn +------- + arry +(1 row) + +select PLVstr.betwn('Harry and Sally are very happy', 'a', 'ry', 1,1,FALSE,FALSE); + betwn +------- + r +(1 row) + +select PLVstr.betwn('Harry and Sally are very happy', 'a', 'ry', 2,1,TRUE,FALSE); + betwn +-------------------- + and Sally are very +(1 row) + +select PLVstr.betwn('Harry and Sally are very happy', 'a', 'y', 2,1); + betwn +----------- + and Sally +(1 row) + +select PLVstr.betwn('Harry and Sally are very happy', 'a', 'a', 2, 2); + betwn +------------- + and Sally a +(1 row) + +select PLVstr.betwn('Harry and Sally are very happy', 'a', 'a', 2, 3, FALSE,FALSE); + betwn +--------------------- + nd Sally are very h +(1 row) + +select plvsubst.string('My name is %s %s.', ARRAY['Pavel','Stěhule']); + string +--------------------------- + My name is Pavel Stěhule. +(1 row) + +select plvsubst.string('My name is % %.', ARRAY['Pavel','Stěhule'], '%'); + string +--------------------------- + My name is Pavel Stěhule. +(1 row) + +select plvsubst.string('My name is %s.', ARRAY['Stěhule']); + string +--------------------- + My name is Stěhule. +(1 row) + +select plvsubst.string('My name is %s %s.', 'Pavel,Stěhule'); + string +--------------------------- + My name is Pavel Stěhule. +(1 row) + +select plvsubst.string('My name is %s %s.', 'Pavel|Stěhule','|'); + string +--------------------------- + My name is Pavel Stěhule. +(1 row) + +select plvsubst.string('My name is %s.', 'Stěhule'); + string +--------------------- + My name is Stěhule. +(1 row) + +select plvsubst.string('My name is %s.', ''); +ERROR: too few parameters specified for template string +select plvsubst.string('My name is empty.', ''); + string +------------------- + My name is empty. +(1 row) + +select round(to_date ('22-AUG-03', 'DD-MON-YY'),'YEAR') = to_date ('01-JAN-04', 'DD-MON-YY'); + ?column? +---------- + t +(1 row) + +select round(to_date ('22-AUG-03', 'DD-MON-YY'),'Q') = to_date ('01-OCT-03', 'DD-MON-YY'); + ?column? +---------- + t +(1 row) + +select round(to_date ('22-AUG-03', 'DD-MON-YY'),'MONTH') = to_date ('01-SEP-03', 'DD-MON-YY'); + ?column? +---------- + t +(1 row) + +select round(to_date ('22-AUG-03', 'DD-MON-YY'),'DDD') = to_date ('22-AUG-03', 'DD-MON-YY'); + ?column? +---------- + t +(1 row) + +select round(to_date ('22-AUG-03', 'DD-MON-YY'),'DAY') = to_date ('24-AUG-03', 'DD-MON-YY'); + ?column? +---------- + t +(1 row) + +select trunc(to_date('22-AUG-03', 'DD-MON-YY'), 'YEAR') = to_date ('01-JAN-03', 'DD-MON-YY'); + ?column? +---------- + t +(1 row) + +select trunc(to_date('22-AUG-03', 'DD-MON-YY'), 'Q') = to_date ('01-JUL-03', 'DD-MON-YY'); + ?column? +---------- + t +(1 row) + +select trunc(to_date('22-AUG-03', 'DD-MON-YY'), 'MONTH') = to_date ('01-AUG-03', 'DD-MON-YY'); + ?column? +---------- + t +(1 row) + +select trunc(to_date('22-AUG-03', 'DD-MON-YY'), 'DDD') = to_date ('22-AUG-03', 'DD-MON-YY'); + ?column? +---------- + t +(1 row) + +select trunc(to_date('22-AUG-03', 'DD-MON-YY'), 'DAY') = to_date ('17-AUG-03', 'DD-MON-YY'); + ?column? +---------- + t +(1 row) + +select trunc(TIMESTAMP WITH TIME ZONE '2004-10-19 10:23:54+02','YEAR') = '2004-01-01 00:00:00-08'; + ?column? +---------- + t +(1 row) + +select trunc(TIMESTAMP WITH TIME ZONE '2004-10-19 10:23:54+02','Q') = '2004-10-01 00:00:00-07'; + ?column? +---------- + t +(1 row) + +select trunc(TIMESTAMP WITH TIME ZONE '2004-10-19 10:23:54+02','MONTH') = '2004-10-01 00:00:00-07'; + ?column? +---------- + t +(1 row) + +select trunc(TIMESTAMP WITH TIME ZONE '2004-10-19 10:23:54+02','DDD') = '2004-10-19 00:00:00-07'; + ?column? +---------- + t +(1 row) + +select trunc(TIMESTAMP WITH TIME ZONE '2004-10-19 10:23:54+02','DAY') = '2004-10-17 00:00:00-07'; + ?column? +---------- + t +(1 row) + +select trunc(TIMESTAMP WITH TIME ZONE '2004-10-19 10:23:54+02','HH') = '2004-10-19 01:00:00-07'; + ?column? +---------- + t +(1 row) + +select trunc(TIMESTAMP WITH TIME ZONE '2004-10-19 10:23:54+02','MI') = '2004-10-19 01:23:00-07'; + ?column? +---------- + t +(1 row) + +select next_day(to_date('01-Aug-03', 'DD-MON-YY'), 'TUESDAY') = to_date ('05-Aug-03', 'DD-MON-YY'); + ?column? +---------- + t +(1 row) + +select next_day(to_date('06-Aug-03', 'DD-MON-YY'), 'WEDNESDAY') = to_date ('13-Aug-03', 'DD-MON-YY'); + ?column? +---------- + t +(1 row) + +select next_day(to_date('06-Aug-03', 'DD-MON-YY'), 'SUNDAY') = to_date ('10-Aug-03', 'DD-MON-YY'); + ?column? +---------- + t +(1 row) + +SET search_path TO oracle,"$user", public, pg_catalog; +select next_day(to_date('01-Aug-03 101111', 'DD-MON-YY h24miss'), 'TUESDAY') = to_date ('05-Aug-03 101111', 'DD-MON-YY h24miss'); + ?column? +---------- + t +(1 row) + +select next_day(to_date('06-Aug-03 10:12:13', 'DD-MON-YY H24:MI:SS'), 'WEDNESDAY') = to_date ('13-Aug-03 10:12:13', 'DD-MON-YY H24:MI:SS'); + ?column? +---------- + t +(1 row) + +select next_day(to_date('06-Aug-03 11:11:11', 'DD-MON-YY HH:MI:SS'), 'SUNDAY') = to_date ('10-Aug-03 11:11:11', 'DD-MON-YY HH:MI:SS'); + ?column? +---------- + t +(1 row) + +SET search_path TO default; +select instr('Tech on the net', 'e') =2; + ?column? +---------- + t +(1 row) + +select instr('Tech on the net', 'e', 1, 1) = 2; + ?column? +---------- + t +(1 row) + +select instr('Tech on the net', 'e', 1, 2) = 11; + ?column? +---------- + t +(1 row) + +select instr('Tech on the net', 'e', 1, 3) = 14; + ?column? +---------- + t +(1 row) + +select instr('Tech on the net', 'e', -3, 2) = 2; + ?column? +---------- + t +(1 row) + +select instr('abc', NULL) IS NULL; + ?column? +---------- + t +(1 row) + +select 1 = instr('abc', ''); + ?column? +---------- + t +(1 row) + +select 1 = instr('abc', 'a'); + ?column? +---------- + t +(1 row) + +select 3 = instr('abc', 'c'); + ?column? +---------- + t +(1 row) + +select 0 = instr('abc', 'z'); + ?column? +---------- + t +(1 row) + +select 1 = instr('abcabcabc', 'abca', 1); + ?column? +---------- + t +(1 row) + +select 4 = instr('abcabcabc', 'abca', 2); + ?column? +---------- + t +(1 row) + +select 0 = instr('abcabcabc', 'abca', 7); + ?column? +---------- + t +(1 row) + +select 0 = instr('abcabcabc', 'abca', 9); + ?column? +---------- + t +(1 row) + +select 4 = instr('abcabcabc', 'abca', -1); + ?column? +---------- + t +(1 row) + +select 1 = instr('abcabcabc', 'abca', -8); + ?column? +---------- + t +(1 row) + +select 1 = instr('abcabcabc', 'abca', -9); + ?column? +---------- + t +(1 row) + +select 0 = instr('abcabcabc', 'abca', -10); + ?column? +---------- + t +(1 row) + +select 1 = instr('abcabcabc', 'abca', 1, 1); + ?column? +---------- + t +(1 row) + +select 4 = instr('abcabcabc', 'abca', 1, 2); + ?column? +---------- + t +(1 row) + +select 0 = instr('abcabcabc', 'abca', 1, 3); + ?column? +---------- + t +(1 row) + +select oracle.substr('This is a test', 6, 2) = 'is'; + ?column? +---------- + t +(1 row) + +select oracle.substr('This is a test', 6) = 'is a test'; + ?column? +---------- + t +(1 row) + +select oracle.substr('TechOnTheNet', 1, 4) = 'Tech'; + ?column? +---------- + t +(1 row) + +select oracle.substr('TechOnTheNet', -3, 3) = 'Net'; + ?column? +---------- + t +(1 row) + +select oracle.substr('TechOnTheNet', -6, 3) = 'The'; + ?column? +---------- + t +(1 row) + +select oracle.substr('TechOnTheNet', -8, 2) = 'On'; + ?column? +---------- + t +(1 row) + +select oracle.substr('TechOnTheNet', -8, 0) = ''; + ?column? +---------- + t +(1 row) + +select oracle.substr('TechOnTheNet', -8, -1) = ''; + ?column? +---------- + +(1 row) + +select oracle.substr(1234567,3.6::smallint)='4567'; + ?column? +---------- + t +(1 row) + +select oracle.substr(1234567,3.6::int)='4567'; + ?column? +---------- + t +(1 row) + +select oracle.substr(1234567,3.6::bigint)='4567'; + ?column? +---------- + t +(1 row) + +select oracle.substr(1234567,3.6::numeric)='34567'; + ?column? +---------- + t +(1 row) + +select oracle.substr(1234567,-1)='7'; + ?column? +---------- + t +(1 row) + +select oracle.substr(1234567,3.6::smallint,2.6)='45'; + ?column? +---------- + t +(1 row) + +select oracle.substr(1234567,3.6::smallint,2.6::smallint)='456'; + ?column? +---------- + t +(1 row) + +select oracle.substr(1234567,3.6::smallint,2.6::int)='456'; + ?column? +---------- + t +(1 row) + +select oracle.substr(1234567,3.6::smallint,2.6::bigint)='456'; + ?column? +---------- + t +(1 row) + +select oracle.substr(1234567,3.6::smallint,2.6::numeric)='45'; + ?column? +---------- + t +(1 row) + +select oracle.substr(1234567,3.6::int,2.6::smallint)='456'; + ?column? +---------- + t +(1 row) + +select oracle.substr(1234567,3.6::int,2.6::int)='456'; + ?column? +---------- + t +(1 row) + +select oracle.substr(1234567,3.6::int,2.6::bigint)='456'; + ?column? +---------- + t +(1 row) + +select oracle.substr(1234567,3.6::int,2.6::numeric)='45'; + ?column? +---------- + t +(1 row) + +select oracle.substr(1234567,3.6::bigint,2.6::smallint)='456'; + ?column? +---------- + t +(1 row) + +select oracle.substr(1234567,3.6::bigint,2.6::int)='456'; + ?column? +---------- + t +(1 row) + +select oracle.substr(1234567,3.6::bigint,2.6::bigint)='456'; + ?column? +---------- + t +(1 row) + +select oracle.substr(1234567,3.6::bigint,2.6::numeric)='45'; + ?column? +---------- + t +(1 row) + +select oracle.substr(1234567,3.6::numeric,2.6::smallint)='345'; + ?column? +---------- + t +(1 row) + +select oracle.substr(1234567,3.6::numeric,2.6::int)='345'; + ?column? +---------- + t +(1 row) + +select oracle.substr(1234567,3.6::numeric,2.6::bigint)='345'; + ?column? +---------- + t +(1 row) + +select oracle.substr(1234567,3.6::numeric,2.6::numeric)='34'; + ?column? +---------- + t +(1 row) + +select oracle.substr('abcdef'::varchar,3.6::smallint)='def'; + ?column? +---------- + t +(1 row) + +select oracle.substr('abcdef'::varchar,3.6::int)='def'; + ?column? +---------- + t +(1 row) + +select oracle.substr('abcdef'::varchar,3.6::bigint)='def'; + ?column? +---------- + t +(1 row) + +select oracle.substr('abcdef'::varchar,3.6::numeric)='cdef'; + ?column? +---------- + t +(1 row) + +select oracle.substr('abcdef'::varchar,3.5::int,3.5::int)='def'; + ?column? +---------- + t +(1 row) + +select oracle.substr('abcdef'::varchar,3.5::numeric,3.5::numeric)='cde'; + ?column? +---------- + t +(1 row) + +select oracle.substr('abcdef'::varchar,3.5::numeric,3.5::int)='cdef'; + ?column? +---------- + t +(1 row) + +select concat('Tech on', ' the Net') = 'Tech on the Net'; + ?column? +---------- + t +(1 row) + +select concat('a', 'b') = 'ab'; + ?column? +---------- + t +(1 row) + +select concat('a', NULL) = 'a'; + ?column? +---------- + t +(1 row) + +select concat(NULL, 'b') = 'b'; + ?column? +---------- + t +(1 row) + +select concat('a', 2) = 'a2'; + ?column? +---------- + t +(1 row) + +select concat(1, 'b') = '1b'; + ?column? +---------- + t +(1 row) + +select concat(1, 2) = '12'; + ?column? +---------- + t +(1 row) + +select concat(1, NULL) = '1'; + ?column? +---------- + t +(1 row) + +select concat(NULL, 2) = '2'; + ?column? +---------- + t +(1 row) + +select nvl('A'::text, 'B'); + nvl +----- + A +(1 row) + +select nvl(NULL::text, 'B'); + nvl +----- + B +(1 row) + +select nvl(NULL::text, NULL); + nvl +----- + +(1 row) + +select nvl(1, 2); + nvl +----- + 1 +(1 row) + +select nvl(NULL, 2); + nvl +----- + 2 +(1 row) + +select nvl2('A'::text, 'B', 'C'); + nvl2 +------ + B +(1 row) + +select nvl2(NULL::text, 'B', 'C'); + nvl2 +------ + C +(1 row) + +select nvl2('A'::text, NULL, 'C'); + nvl2 +------ + +(1 row) + +select nvl2(NULL::text, 'B', NULL); + nvl2 +------ + +(1 row) + +select nvl2(1, 2, 3); + nvl2 +------ + 2 +(1 row) + +select nvl2(NULL, 2, 3); + nvl2 +------ + 3 +(1 row) + +select lnnvl(true); + lnnvl +------- + f +(1 row) + +select lnnvl(false); + lnnvl +------- + t +(1 row) + +select lnnvl(NULL); + lnnvl +------- + t +(1 row) + +select decode(1, 1, 100, 2, 200); + decode +-------- + 100 +(1 row) + +select decode(2, 1, 100, 2, 200); + decode +-------- + 200 +(1 row) + +select decode(3, 1, 100, 2, 200); + decode +-------- + +(1 row) + +select decode(3, 1, 100, 2, 200, 300); + decode +-------- + 300 +(1 row) + +select decode(NULL, 1, 100, NULL, 200, 300); + decode +-------- + 200 +(1 row) + +select decode('1'::text, '1', 100, '2', 200); + decode +-------- + 100 +(1 row) + +select decode(2, 1, 'ABC', 2, 'DEF'); + decode +-------- + DEF +(1 row) + +select decode('2009-02-05'::date, '2009-02-05', 'ok'); + decode +-------- + ok +(1 row) + +select decode('2009-02-05 01:02:03'::timestamp, '2009-02-05 01:02:03', 'ok'); + decode +-------- + ok +(1 row) + +-- For type 'bpchar' +select decode('a'::bpchar, 'a'::bpchar,'postgres'::bpchar); + decode +---------- + postgres +(1 row) + +select decode('c'::bpchar, 'a'::bpchar,'postgres'::bpchar); + decode +-------- + +(1 row) + +select decode('a'::bpchar, 'a'::bpchar,'postgres'::bpchar,'default value'::bpchar); + decode +---------- + postgres +(1 row) + +select decode('c', 'a'::bpchar,'postgres'::bpchar,'default value'::bpchar); + decode +--------------- + default value +(1 row) + +select decode('a'::bpchar, 'a'::bpchar,'postgres'::bpchar,'b'::bpchar,'database'::bpchar); + decode +---------- + postgres +(1 row) + +select decode('d'::bpchar, 'a'::bpchar,'postgres'::bpchar,'b'::bpchar,'database'::bpchar); + decode +-------- + +(1 row) + +select decode('a'::bpchar, 'a'::bpchar,'postgres'::bpchar,'b'::bpchar,'database'::bpchar,'default value'::bpchar); + decode +---------- + postgres +(1 row) + +select decode('d'::bpchar, 'a'::bpchar,'postgres'::bpchar,'b'::bpchar,'database'::bpchar,'default value'::bpchar); + decode +--------------- + default value +(1 row) + +select decode('a'::bpchar, 'a'::bpchar,'postgres'::bpchar,'b'::bpchar,'database'::bpchar, 'c'::bpchar, 'system'::bpchar); + decode +---------- + postgres +(1 row) + +select decode('d'::bpchar, 'a'::bpchar,'postgres'::bpchar,'b'::bpchar,'database'::bpchar, 'c'::bpchar, 'system'::bpchar); + decode +-------- + +(1 row) + +select decode('a'::bpchar, 'a'::bpchar,'postgres'::bpchar,'b'::bpchar,'database'::bpchar, 'c'::bpchar, 'system'::bpchar,'default value'::bpchar); + decode +---------- + postgres +(1 row) + +select decode('d'::bpchar, 'a'::bpchar,'postgres'::bpchar,'b'::bpchar,'database'::bpchar, 'c'::bpchar, 'system'::bpchar,'default value'::bpchar); + decode +--------------- + default value +(1 row) + +select decode(NULL, 'a'::bpchar, 'postgres'::bpchar, NULL,'database'::bpchar); + decode +---------- + database +(1 row) + +select decode(NULL, 'a'::bpchar, 'postgres'::bpchar, 'b'::bpchar,'database'::bpchar); + decode +-------- + +(1 row) + +select decode(NULL, 'a'::bpchar, 'postgres'::bpchar, NULL,'database'::bpchar,'default value'::bpchar); + decode +---------- + database +(1 row) + +select decode(NULL, 'a'::bpchar, 'postgres'::bpchar, 'b'::bpchar,'database'::bpchar,'default value'::bpchar); + decode +--------------- + default value +(1 row) + +-- For type 'bigint' +select decode(2147483651::bigint, 2147483650::bigint,2147483650::bigint); + decode +-------- + +(1 row) + +select decode(2147483653::bigint, 2147483651::bigint,2147483650::bigint); + decode +-------- + +(1 row) + +select decode(2147483653::bigint, 2147483651::bigint,2147483650::bigint,9999999999::bigint); + decode +------------ + 9999999999 +(1 row) + +select decode(2147483653::bigint, 2147483651::bigint,2147483650::bigint,9999999999::bigint); + decode +------------ + 9999999999 +(1 row) + +select decode(2147483651::bigint, 2147483651::bigint,2147483650::bigint,2147483652::bigint,2147483651::bigint); + decode +------------ + 2147483650 +(1 row) + +select decode(2147483654::bigint, 2147483651::bigint,2147483650::bigint,2147483652::bigint,2147483651::bigint); + decode +-------- + +(1 row) + +select decode(2147483651::bigint, 2147483651::bigint,2147483650::bigint,2147483652::bigint,2147483651::bigint,9999999999::bigint); + decode +------------ + 2147483650 +(1 row) + +select decode(2147483654::bigint, 2147483651::bigint,2147483650::bigint,2147483652::bigint,2147483651::bigint,9999999999::bigint); + decode +------------ + 9999999999 +(1 row) + +select decode(2147483651::bigint, 2147483651::bigint,2147483650::bigint, 2147483652::bigint,2147483651::bigint, 2147483653::bigint, 2147483652::bigint); + decode +------------ + 2147483650 +(1 row) + +select decode(2147483654::bigint, 2147483651::bigint,2147483650::bigint, 2147483652::bigint,2147483651::bigint, 2147483653::bigint, 2147483652::bigint); + decode +-------- + +(1 row) + +select decode(2147483651::bigint, 2147483651::bigint,2147483650::bigint, 2147483652::bigint,2147483651::bigint, 2147483653::bigint, 2147483652::bigint,9999999999::bigint); + decode +------------ + 2147483650 +(1 row) + +select decode(2147483654::bigint, 2147483651::bigint,2147483650::bigint, 2147483652::bigint,2147483651::bigint, 2147483653::bigint, 2147483652::bigint,9999999999::bigint); + decode +------------ + 9999999999 +(1 row) + +select decode(NULL, 2147483651::bigint, 2147483650::bigint, NULL,2147483651::bigint); + decode +------------ + 2147483651 +(1 row) + +select decode(NULL, 2147483651::bigint, 2147483650::bigint, 2147483652::bigint,2147483651::bigint); + decode +-------- + +(1 row) + +select decode(NULL, 2147483651::bigint, 2147483650::bigint, NULL,2147483651::bigint,9999999999::bigint); + decode +------------ + 2147483651 +(1 row) + +select decode(NULL, 2147483651::bigint, 2147483650::bigint, 2147483652::bigint,2147483651::bigint,9999999999::bigint); + decode +------------ + 9999999999 +(1 row) + +-- For type 'numeric' +select decode(12.001::numeric(5,3), 12.001::numeric(5,3),214748.3650::numeric(10,4)); + decode +------------- + 214748.3650 +(1 row) + +select decode(12.003::numeric(5,3), 12.001::numeric(5,3),214748.3650::numeric(10,4)); + decode +-------- + +(1 row) + +select decode(12.001::numeric(5,3), 12.001::numeric(5,3),214748.3650::numeric(10,4),999999.9999::numeric(10,4)); + decode +------------- + 214748.3650 +(1 row) + +select decode(12.003::numeric(5,3), 12.001::numeric(5,3),214748.3650::numeric(10,4),999999.9999::numeric(10,4)); + decode +------------- + 999999.9999 +(1 row) + +select decode(12.001::numeric(5,3), 12.001::numeric(5,3),214748.3650::numeric(10,4),12.002::numeric(5,3),214748.3651::numeric(10,4)); + decode +------------- + 214748.3650 +(1 row) + +select decode(12.004::numeric(5,3), 12.001::numeric(5,3),214748.3650::numeric(10,4),12.002::numeric(5,3),214748.3651::numeric(10,4)); + decode +-------- + +(1 row) + +select decode(12.001::numeric(5,3), 12.001::numeric(5,3),214748.3650::numeric(10,4),12.002::numeric(5,3),214748.3651::numeric(10,4),999999.9999::numeric(10,4)); + decode +------------- + 214748.3650 +(1 row) + +select decode(12.004::numeric(5,3), 12.001::numeric(5,3),214748.3650::numeric(10,4),12.002::numeric(5,3),214748.3651::numeric(10,4),999999.9999::numeric(10,4)); + decode +------------- + 999999.9999 +(1 row) + +select decode(12.001::numeric(5,3), 12.001::numeric(5,3),214748.3650::numeric(10,4),12.002::numeric(5,3),214748.3651::numeric(10,4), 12.003::numeric(5,3), 214748.3652::numeric(10,4)); + decode +------------- + 214748.3650 +(1 row) + +select decode(12.004::numeric(5,3), 12.001::numeric(5,3),214748.3650::numeric(10,4),12.002::numeric(5,3),214748.3651::numeric(10,4), 12.003::numeric(5,3), 214748.3652::numeric(10,4)); + decode +-------- + +(1 row) + +select decode(12.001::numeric(5,3), 12.001::numeric(5,3),214748.3650::numeric(10,4),12.002::numeric(5,3),214748.3651::numeric(10,4), 12.003::numeric(5,3), 214748.3652::numeric(10,4),999999.9999::numeric(10,4)); + decode +------------- + 214748.3650 +(1 row) + +select decode(12.004::numeric(5,3), 12.001::numeric(5,3),214748.3650::numeric(10,4),12.002::numeric(5,3),214748.3651::numeric(10,4), 12.003::numeric(5,3), 214748.3652::numeric(10,4),999999.9999::numeric(10,4)); + decode +------------- + 999999.9999 +(1 row) + +select decode(NULL, 12.001::numeric(5,3), 214748.3650::numeric(10,4), NULL,214748.3651::numeric(10,4)); + decode +------------- + 214748.3651 +(1 row) + +select decode(NULL, 12.001::numeric(5,3), 214748.3650::numeric(10,4), 12.002::numeric(5,3),214748.3651::numeric(10,4)); + decode +-------- + +(1 row) + +select decode(NULL, 12.001::numeric(5,3), 214748.3650::numeric(10,4), NULL,214748.3651::numeric(10,4),999999.9999::numeric(10,4)); + decode +------------- + 214748.3651 +(1 row) + +select decode(NULL, 12.001::numeric(5,3), 214748.3650::numeric(10,4), 12.002::numeric(5,3),214748.3651::numeric(10,4),999999.9999::numeric(10,4)); + decode +------------- + 999999.9999 +(1 row) + +--For type 'date' +select decode('2020-01-01'::date, '2020-01-01'::date,'2012-12-20'::date); + decode +------------ + 2012-12-20 +(1 row) + +select decode('2020-01-03'::date, '2020-01-01'::date,'2012-12-20'::date); + decode +-------- + +(1 row) + +select decode('2020-01-01'::date, '2020-01-01'::date,'2012-12-20'::date,'2012-12-21'::date); + decode +------------ + 2012-12-20 +(1 row) + +select decode('2020-01-03'::date, '2020-01-01'::date,'2012-12-20'::date,'2012-12-21'::date); + decode +------------ + 2012-12-21 +(1 row) + +select decode('2020-01-01'::date, '2020-01-01'::date,'2012-12-20'::date,'2020-01-02'::date,'2012-12-21'::date); + decode +------------ + 2012-12-20 +(1 row) + +select decode('2020-01-04'::date, '2020-01-01'::date,'2012-12-20'::date,'2020-01-02'::date,'2012-12-21'::date); + decode +-------- + +(1 row) + +select decode('2020-01-01'::date, '2020-01-01'::date,'2012-12-20'::date,'2020-01-02'::date,'2012-12-21'::date,'2012-12-31'::date); + decode +------------ + 2012-12-20 +(1 row) + +select decode('2020-01-04'::date, '2020-01-01'::date,'2012-12-20'::date,'2020-01-02'::date,'2012-12-21'::date,'2012-12-31'::date); + decode +------------ + 2012-12-31 +(1 row) + +select decode('2020-01-01'::date, '2020-01-01'::date,'2012-12-20'::date,'2020-01-02'::date,'2012-12-21'::date, '2020-01-03'::date, '2012-12-31'::date); + decode +------------ + 2012-12-20 +(1 row) + +select decode('2020-01-04'::date, '2020-01-01'::date,'2012-12-20'::date,'2020-01-02'::date,'2012-12-21'::date, '2020-01-03'::date, '2012-12-31'::date); + decode +-------- + +(1 row) + +select decode('2020-01-01'::date, '2020-01-01'::date,'2012-12-20'::date,'2020-01-02'::date,'2012-12-21'::date, '2020-01-03'::date, '2012-12-31'::date,'2013-01-01'::date); + decode +------------ + 2012-12-20 +(1 row) + +select decode('2020-01-04'::date, '2020-01-01'::date,'2012-12-20'::date,'2020-01-02'::date,'2012-12-21'::date, '2020-01-03'::date, '2012-12-31'::date,'2013-01-01'::date); + decode +------------ + 2013-01-01 +(1 row) + +select decode(NULL, '2020-01-01'::date, '2012-12-20'::date, NULL,'2012-12-21'::date); + decode +------------ + 2012-12-21 +(1 row) + +select decode(NULL, '2020-01-01'::date, '2012-12-20'::date, '2020-01-02'::date,'2012-12-21'::date); + decode +-------- + +(1 row) + +select decode(NULL, '2020-01-01'::date, '2012-12-20'::date, NULL,'2012-12-21'::date,'2012-12-31'::date); + decode +------------ + 2012-12-21 +(1 row) + +select decode(NULL, '2020-01-01'::date, '2012-12-20'::date, '2020-01-02'::date,'2012-12-21'::date,'2012-12-31'::date); + decode +------------ + 2012-12-31 +(1 row) + +-- For type 'time' +select decode('01:00:01'::time, '01:00:01'::time,'09:00:00'::time); + decode +---------- + 09:00:00 +(1 row) + +select decode('01:00:03'::time, '01:00:01'::time,'09:00:00'::time); + decode +-------- + +(1 row) + +select decode('01:00:01'::time, '01:00:01'::time,'09:00:00'::time,'00:00:00'::time); + decode +---------- + 09:00:00 +(1 row) + +select decode('01:00:03'::time, '01:00:01'::time,'09:00:00'::time,'00:00:00'::time); + decode +---------- + 00:00:00 +(1 row) + +select decode('01:00:01'::time, '01:00:01'::time,'09:00:00'::time,'01:00:02'::time,'12:00:00'::time); + decode +---------- + 09:00:00 +(1 row) + +select decode('01:00:04'::time, '01:00:01'::time,'09:00:00'::time,'01:00:02'::time,'12:00:00'::time); + decode +-------- + +(1 row) + +select decode('01:00:01'::time, '01:00:01'::time,'09:00:00'::time,'01:00:02'::time,'12:00:00'::time,'00:00:00'::time); + decode +---------- + 09:00:00 +(1 row) + +select decode('01:00:04'::time, '01:00:01'::time,'09:00:00'::time,'01:00:01'::time,'12:00:00'::time,'00:00:00'::time); + decode +---------- + 00:00:00 +(1 row) + +select decode('01:00:01'::time, '01:00:01'::time,'09:00:00'::time,'01:00:02'::time,'12:00:00'::time, '01:00:03'::time, '15:00:00'::time); + decode +---------- + 09:00:00 +(1 row) + +select decode('01:00:04'::time, '01:00:01'::time,'09:00:00'::time,'01:00:02'::time,'12:00:00'::time, '01:00:03'::time, '15:00:00'::time); + decode +-------- + +(1 row) + +select decode('01:00:01'::time, '01:00:01'::time,'09:00:00'::time,'01:00:02'::time,'12:00:00'::time, '01:00:03'::time, '15:00:00'::time,'00:00:00'::time); + decode +---------- + 09:00:00 +(1 row) + +select decode('01:00:04'::time, '01:00:01'::time,'09:00:00'::time,'01:00:02'::time,'12:00:00'::time, '01:00:03'::time, '15:00:00'::time,'00:00:00'::time); + decode +---------- + 00:00:00 +(1 row) + +select decode(NULL, '01:00:01'::time, '09:00:00'::time, NULL,'12:00:00'::time); + decode +---------- + 12:00:00 +(1 row) + +select decode(NULL, '01:00:01'::time, '09:00:00'::time, '01:00:02'::time,'12:00:00'::time); + decode +-------- + +(1 row) + +select decode(NULL, '01:00:01'::time, '09:00:00'::time, NULL,'12:00:00'::time,'00:00:00'::time); + decode +---------- + 12:00:00 +(1 row) + +select decode(NULL, '01:00:01'::time, '09:00:00'::time, '01:00:02'::time,'12:00:00'::time,'00:00:00'::time); + decode +---------- + 00:00:00 +(1 row) + +-- For type 'timestamp' +select decode('2020-01-01 01:00:01'::timestamp, '2020-01-01 01:00:01'::timestamp,'2012-12-20 09:00:00'::timestamp); + decode +--------------------- + 2012-12-20 09:00:00 +(1 row) + +select decode('2020-01-03 01:00:01'::timestamp, '2020-01-01 01:00:01'::timestamp,'2012-12-20 09:00:00'::timestamp); + decode +-------- + +(1 row) + +select decode('2020-01-01 01:00:01'::timestamp, '2020-01-01 01:00:01'::timestamp,'2012-12-20 09:00:00'::timestamp,'2012-12-20 00:00:00'::timestamp); + decode +--------------------- + 2012-12-20 09:00:00 +(1 row) + +select decode('2020-01-03 01:00:01'::timestamp, '2020-01-01 01:00:01'::timestamp,'2012-12-20 09:00:00'::timestamp,'2012-12-20 00:00:00'::timestamp); + decode +--------------------- + 2012-12-20 00:00:00 +(1 row) + +select decode('2020-01-01 01:00:01'::timestamp, '2020-01-01 01:00:01'::timestamp,'2012-12-20 09:00:00'::timestamp,'2020-01-02 01:00:01'::timestamp,'2012-12-20 12:00:00'::timestamp); + decode +--------------------- + 2012-12-20 09:00:00 +(1 row) + +select decode('2020-01-04 01:00:01'::timestamp, '2020-01-01 01:00:01'::timestamp,'2012-12-20 09:00:00'::timestamp,'2020-01-02 01:00:01'::timestamp,'2012-12-20 12:00:00'::timestamp); + decode +-------- + +(1 row) + +select decode('2020-01-01 01:00:01'::timestamp, '2020-01-01 01:00:01'::timestamp,'2012-12-20 09:00:00'::timestamp,'2020-01-02 01:00:01'::timestamp,'2012-12-20 12:00:00'::timestamp,'2012-12-20 00:00:00'::timestamp); + decode +--------------------- + 2012-12-20 09:00:00 +(1 row) + +select decode('2020-01-04 01:00:01'::timestamp, '2020-01-01 01:00:01'::timestamp,'2012-12-20 09:00:00'::timestamp,'2020-01-02 01:00:01'::timestamp,'2012-12-20 12:00:00'::timestamp,'2012-12-20 00:00:00'::timestamp); + decode +--------------------- + 2012-12-20 00:00:00 +(1 row) + +select decode('2020-01-01 01:00:01'::timestamp, '2020-01-01 01:00:01'::timestamp,'2012-12-20 09:00:00'::timestamp,'2020-01-02 01:00:01'::timestamp,'2012-12-20 12:00:00'::timestamp, '2020-01-03 01:00:01'::timestamp, '2012-12-20 15:00:00'::timestamp); + decode +--------------------- + 2012-12-20 09:00:00 +(1 row) + +select decode('2020-01-04 01:00:01'::timestamp, '2020-01-01 01:00:01'::timestamp,'2012-12-20 09:00:00'::timestamp,'2020-01-02 01:00:01'::timestamp,'2012-12-20 12:00:00'::timestamp, '2020-01-03 01:00:01'::timestamp, '2012-12-20 15:00:00'::timestamp); + decode +-------- + +(1 row) + +select decode('2020-01-01 01:00:01'::timestamp, '2020-01-01 01:00:01'::timestamp,'2012-12-20 09:00:00'::timestamp,'2020-01-02 01:00:01'::timestamp,'2012-12-20 12:00:00'::timestamp, '2020-01-03 01:00:01'::timestamp, '2012-12-20 15:00:00'::timestamp,'2012-12-20 00:00:00'::timestamp); + decode +--------------------- + 2012-12-20 09:00:00 +(1 row) + +select decode('2020-01-04 01:00:01'::timestamp, '2020-01-01 01:00:01'::timestamp,'2012-12-20 09:00:00'::timestamp,'2020-01-02 01:00:01'::timestamp,'2012-12-20 12:00:00'::timestamp, '2020-01-03 01:00:01'::timestamp, '2012-12-20 15:00:00'::timestamp,'2012-12-20 00:00:00'::timestamp); + decode +--------------------- + 2012-12-20 00:00:00 +(1 row) + +select decode(NULL, '2020-01-01 01:00:01'::timestamp, '2012-12-20 09:00:00'::timestamp, NULL,'2012-12-20 12:00:00'::timestamp); + decode +--------------------- + 2012-12-20 12:00:00 +(1 row) + +select decode(NULL, '2020-01-01 01:00:01'::timestamp, '2012-12-20 09:00:00'::timestamp, '2020-01-02 01:00:01'::timestamp,'2012-12-20 12:00:00'::timestamp); + decode +-------- + +(1 row) + +select decode(NULL, '2020-01-01 01:00:01'::timestamp, '2012-12-20 09:00:00'::timestamp, NULL,'2012-12-20 12:00:00'::timestamp,'2012-12-20 00:00:00'::timestamp); + decode +--------------------- + 2012-12-20 12:00:00 +(1 row) + +select decode(NULL, '2020-01-01 01:00:01'::timestamp, '2012-12-20 09:00:00'::timestamp, '2020-01-02 01:00:01'::timestamp,'2012-12-20 12:00:00'::timestamp,'2012-12-20 00:00:00'::timestamp); + decode +--------------------- + 2012-12-20 00:00:00 +(1 row) + +-- For type 'timestamptz' +select decode('2020-01-01 01:00:01-08'::timestamptz, '2020-01-01 01:00:01-08'::timestamptz,'2012-12-20 09:00:00-08'::timestamptz); + decode +------------------------ + 2012-12-20 09:00:00-08 +(1 row) + +select decode('2020-01-03 01:00:01-08'::timestamptz, '2020-01-01 01:00:01-08'::timestamptz,'2012-12-20 09:00:00-08'::timestamptz); + decode +-------- + +(1 row) + +select decode('2020-01-01 01:00:01-08'::timestamptz, '2020-01-01 01:00:01-08'::timestamptz,'2012-12-20 09:00:00-08'::timestamptz,'2012-12-20 00:00:00-08'::timestamptz); + decode +------------------------ + 2012-12-20 09:00:00-08 +(1 row) + +select decode('2020-01-03 01:00:01-08'::timestamptz, '2020-01-01 01:00:01-08'::timestamptz,'2012-12-20 09:00:00-08'::timestamptz,'2012-12-20 00:00:00-08'::timestamptz); + decode +------------------------ + 2012-12-20 00:00:00-08 +(1 row) + +select decode('2020-01-01 01:00:01-08'::timestamptz, '2020-01-01 01:00:01-08'::timestamptz,'2012-12-20 09:00:00-08'::timestamptz,'2020-01-02 01:00:01-08'::timestamptz,'2012-12-20 12:00:00-08'::timestamptz); + decode +------------------------ + 2012-12-20 09:00:00-08 +(1 row) + +select decode('2020-01-04 01:00:01-08'::timestamptz, '2020-01-01 01:00:01-08'::timestamptz,'2012-12-20 09:00:00-08'::timestamptz,'2020-01-02 01:00:01-08'::timestamptz,'2012-12-20 12:00:00-08'::timestamptz); + decode +-------- + +(1 row) + +select decode('2020-01-01 01:00:01-08'::timestamptz, '2020-01-01 01:00:01-08'::timestamptz,'2012-12-20 09:00:00-08'::timestamptz,'2020-01-02 01:00:01-08'::timestamptz,'2012-12-20 12:00:00-08'::timestamptz,'2012-12-20 00:00:00-08'::timestamptz); + decode +------------------------ + 2012-12-20 09:00:00-08 +(1 row) + +select decode('2020-01-04 01:00:01-08'::timestamptz, '2020-01-01 01:00:01-08'::timestamptz,'2012-12-20 09:00:00-08'::timestamptz,'2020-01-02 01:00:01-08'::timestamptz,'2012-12-20 12:00:00-08'::timestamptz,'2012-12-20 00:00:00-08'::timestamptz); + decode +------------------------ + 2012-12-20 00:00:00-08 +(1 row) + +select decode('2020-01-01 01:00:01-08'::timestamptz, '2020-01-01 01:00:01-08'::timestamptz,'2012-12-20 09:00:00-08'::timestamptz,'2020-01-02 01:00:01-08'::timestamptz,'2012-12-20 12:00:00-08'::timestamptz, '2020-01-03 01:00:01-08'::timestamptz, '2012-12-20 15:00:00-08'::timestamptz); + decode +------------------------ + 2012-12-20 09:00:00-08 +(1 row) + +select decode('2020-01-04 01:00:01-08'::timestamptz, '2020-01-01 01:00:01-08'::timestamptz,'2012-12-20 09:00:00-08'::timestamptz,'2020-01-02 01:00:01-08'::timestamptz,'2012-12-20 12:00:00-08'::timestamptz, '2020-01-03 01:00:01-08'::timestamptz, '2012-12-20 15:00:00-08'::timestamptz); + decode +-------- + +(1 row) + +select decode('2020-01-01 01:00:01-08'::timestamptz, '2020-01-01 01:00:01-08'::timestamptz,'2012-12-20 09:00:00-08'::timestamptz,'2020-01-02 01:00:01-08'::timestamptz,'2012-12-20 12:00:00-08'::timestamptz, '2020-01-03 01:00:01-08'::timestamptz, '2012-12-20 15:00:00-08'::timestamptz,'2012-12-20 00:00:00-08'::timestamptz); + decode +------------------------ + 2012-12-20 09:00:00-08 +(1 row) + +select decode(4, 1,'2012-12-20 09:00:00-08'::timestamptz,2,'2012-12-20 12:00:00-08'::timestamptz, 3, '2012-12-20 15:00:00-08'::timestamptz,'2012-12-20 00:00:00-08'::timestamptz); + decode +------------------------ + 2012-12-20 00:00:00-08 +(1 row) + +select decode(NULL, '2020-01-01 01:00:01-08'::timestamptz, '2012-12-20 09:00:00-08'::timestamptz, NULL,'2012-12-20 12:00:00-08'::timestamptz); + decode +------------------------ + 2012-12-20 12:00:00-08 +(1 row) + +select decode(NULL, '2020-01-01 01:00:01-08'::timestamptz, '2012-12-20 09:00:00-08'::timestamptz, '2020-01-02 01:00:01-08'::timestamptz,'2012-12-20 12:00:00-08'::timestamptz); + decode +-------- + +(1 row) + +select decode(NULL, '2020-01-01 01:00:01-08'::timestamptz, '2012-12-20 09:00:00-08'::timestamptz, NULL,'2012-12-20 12:00:00-08'::timestamptz,'2012-12-20 00:00:00-08'::timestamptz); + decode +------------------------ + 2012-12-20 12:00:00-08 +(1 row) + +select decode(NULL, '2020-01-01 01:00:01-08'::timestamptz, '2012-12-20 09:00:00-08'::timestamptz, '2020-01-02 01:00:01-08'::timestamptz,'2012-12-20 12:00:00-08'::timestamptz,'2012-12-20 00:00:00-08'::timestamptz); + decode +------------------------ + 2012-12-20 00:00:00-08 +(1 row) + +--Test case to check if decode accepts other expressions as a key +CREATE OR REPLACE FUNCTION five() RETURNS integer AS $$ +BEGIN + RETURN 5; +END; +$$ LANGUAGE plpgsql; +select decode(five(), 1, 'one', 2, 'two', 5, 'five'); + decode +-------- + five +(1 row) + +DROP FUNCTION five(); +-- Test case to check duplicate keys in search list +select decode(1, 1, 'one', 2, 'two', 1, 'one-again') = 'one'; + ?column? +---------- + t +(1 row) + +/* Test case to check explicit type casting of keys in search list in + * case of ambiguous key (1st argument) provided. + */ +-- 1) succeed and return 'result-1' +select decode('2012-01-01', '2012-01-01'::date,'result-1','2012-01-02', 'result-2'); + decode +---------- + result-1 +(1 row) + +select decode('2012-01-01', '2012-01-01', 'result-1', '2012-02-01'::date, 'result-2'); + decode +---------- + result-1 +(1 row) + +select PLVstr.rvrs ('Jumping Jack Flash') ='hsalF kcaJ gnipmuJ'; + ?column? +---------- + t +(1 row) + +select PLVstr.rvrs ('Jumping Jack Flash', 9) = 'hsalF kcaJ'; + ?column? +---------- + t +(1 row) + +select PLVstr.rvrs ('Jumping Jack Flash', 4, 6) = 'nip'; + ?column? +---------- + t +(1 row) + +select PLVstr.rvrs (NULL, 10, 20); + rvrs +------ + +(1 row) + +select PLVstr.rvrs ('alphabet', -2, -5); + rvrs +------ + ebah +(1 row) + +select PLVstr.rvrs ('alphabet', -2); + rvrs +--------- + ebahpla +(1 row) + +select PLVstr.rvrs ('alphabet', 2, 200); + rvrs +--------- + tebahpl +(1 row) + +select PLVstr.rvrs ('alphabet', 20, 200); + rvrs +------ + +(1 row) + +select PLVstr.lstrip ('*val1|val2|val3|*', '*') = 'val1|val2|val3|*'; + ?column? +---------- + t +(1 row) + +select PLVstr.lstrip (',,,val1,val2,val3,', ',', 3)= 'val1,val2,val3,'; + ?column? +---------- + t +(1 row) + +select PLVstr.lstrip ('WHERE WHITE = ''FRONT'' AND COMP# = 1500', 'WHERE ') = 'WHITE = ''FRONT'' AND COMP# = 1500'; + ?column? +---------- + t +(1 row) + +select plvstr.left('Příliš žluťoučký kůň',4) = pg_catalog.substr('Příl', 1, 4); + ?column? +---------- + t +(1 row) + +select pos,token from plvlex.tokens('select * from a.b.c join d ON x=y', true, true); + pos | token +-----+-------- + 0 | select + 7 | * + 9 | from + 14 | a.b.c + 20 | join + 25 | d + 27 | on + 30 | x + 31 | = + 32 | y +(10 rows) + +SET lc_numeric TO 'C'; +select to_char(22); + to_char +--------- + 22 +(1 row) + +select to_char(99::smallint); + to_char +--------- + 99 +(1 row) + +select to_char(-44444); + to_char +--------- + -44444 +(1 row) + +select to_char(1234567890123456::bigint); + to_char +------------------ + 1234567890123456 +(1 row) + +select to_char(123.456::real); + to_char +--------- + 123.456 +(1 row) + +select to_char(1234.5678::double precision); + to_char +----------- + 1234.5678 +(1 row) + +select to_char(12345678901234567890::numeric); + to_char +---------------------- + 12345678901234567890 +(1 row) + +select to_char(1234567890.12345); + to_char +------------------ + 1234567890.12345 +(1 row) + +select to_char('4.00'::numeric); + to_char +--------- + 4 +(1 row) + +select to_char('4.0010'::numeric); + to_char +--------- + 4.001 +(1 row) + +SELECT to_number('123'::text); + to_number +----------- + 123 +(1 row) + +SELECT to_number('123.456'::text); + to_number +----------- + 123.456 +(1 row) + +SELECT to_number(123); + to_number +----------- + 123 +(1 row) + +SELECT to_number(123::smallint); + to_number +----------- + 123 +(1 row) + +SELECT to_number(123::int); + to_number +----------- + 123 +(1 row) + +SELECT to_number(123::bigint); + to_number +----------- + 123 +(1 row) + +SELECT to_number(123::numeric); + to_number +----------- + 123 +(1 row) + +SELECT to_number(123.456); + to_number +----------- + 123.456 +(1 row) + +SELECT to_number(1210.73, 9999.99); + to_number +----------- + 1210.73 +(1 row) + +SELECT to_number(1210::smallint, 9999::smallint); + to_number +----------- + 1210 +(1 row) + +SELECT to_number(1210::int, 9999::int); + to_number +----------- + 1210 +(1 row) + +SELECT to_number(1210::bigint, 9999::bigint); + to_number +----------- + 1210 +(1 row) + +SELECT to_number(1210.73::numeric, 9999.99::numeric); + to_number +----------- + 1210.73 +(1 row) + +SELECT to_date('2009-01-02'); + to_date +--------------------- + 2009-01-02 00:00:00 +(1 row) + +SELECT bitand(5,1), bitand(5,2), bitand(5,4); + bitand | bitand | bitand +--------+--------+-------- + 1 | 0 | 4 +(1 row) + +SELECT sinh(1.570796)::numeric(10, 8), cosh(1.570796)::numeric(10, 8), tanh(4)::numeric(10, 8); + sinh | cosh | tanh +------------+------------+------------ + 2.30129808 | 2.50917773 | 0.99932930 +(1 row) + +SELECT nanvl(12345, 1), nanvl('NaN', 1); + nanvl | nanvl +-------+------- + 12345 | 1 +(1 row) + +SELECT nanvl(12345::float4, 1), nanvl('NaN'::float4, 1); + nanvl | nanvl +-------+------- + 12345 | 1 +(1 row) + +SELECT nanvl(12345::float8, 1), nanvl('NaN'::float8, 1); + nanvl | nanvl +-------+------- + 12345 | 1 +(1 row) + +SELECT nanvl(12345::numeric, 1), nanvl('NaN'::numeric, 1); + nanvl | nanvl +-------+------- + 12345 | 1 +(1 row) + +SELECT nanvl(12345, '1'::varchar), nanvl('NaN', 1::varchar); + nanvl | nanvl +-------+------- + 12345 | 1 +(1 row) + +SELECT nanvl(12345::float4, '1'::varchar), nanvl('NaN'::float4, '1'::varchar); + nanvl | nanvl +-------+------- + 12345 | 1 +(1 row) + +SELECT nanvl(12345::float8, '1'::varchar), nanvl('NaN'::float8, '1'::varchar); + nanvl | nanvl +-------+------- + 12345 | 1 +(1 row) + +SELECT nanvl(12345::numeric, '1'::varchar), nanvl('NaN'::numeric, '1'::varchar); + nanvl | nanvl +-------+------- + 12345 | 1 +(1 row) + +SELECT nanvl(12345, '1'::char), nanvl('NaN', 1::char); + nanvl | nanvl +-------+------- + 12345 | 1 +(1 row) + +SELECT nanvl(12345::float4, '1'::char), nanvl('NaN'::float4, '1'::char); + nanvl | nanvl +-------+------- + 12345 | 1 +(1 row) + +SELECT nanvl(12345::float8, '1'::char), nanvl('NaN'::float8, '1'::char); + nanvl | nanvl +-------+------- + 12345 | 1 +(1 row) + +SELECT nanvl(12345::numeric, '1'::char), nanvl('NaN'::numeric, '1'::char); + nanvl | nanvl +-------+------- + 12345 | 1 +(1 row) + +select dbms_assert.enquote_literal('some text '' some text'); + enquote_literal +-------------------------- + 'some text '' some text' +(1 row) + +select dbms_assert.enquote_name('''"AAA'); + enquote_name +-------------- + "'""aaa" +(1 row) + +select dbms_assert.enquote_name('''"AAA', false); + enquote_name +-------------- + "'""AAA" +(1 row) + +select dbms_assert.noop('some string'); + noop +------------- + some string +(1 row) + +select dbms_assert.qualified_sql_name('aaa.bbb.ccc."aaaa""aaa"'); + qualified_sql_name +------------------------- + aaa.bbb.ccc."aaaa""aaa" +(1 row) + +select dbms_assert.qualified_sql_name('aaa.bbb.cc%c."aaaa""aaa"'); +ERROR: string is not qualified SQL name +select dbms_assert.schema_name('dbms_assert'); + schema_name +------------- + dbms_assert +(1 row) + +select dbms_assert.schema_name('jabadabado'); +ERROR: invalid schema name +select dbms_assert.simple_sql_name('"Aaa dghh shsh"'); + simple_sql_name +----------------- + "Aaa dghh shsh" +(1 row) + +select dbms_assert.simple_sql_name('ajajaj -- ajaj'); +ERROR: string is not simple SQL name +select dbms_assert.object_name('pg_catalog.pg_class'); + object_name +--------------------- + pg_catalog.pg_class +(1 row) + +select dbms_assert.object_name('dbms_assert.fooo'); +ERROR: invalid object name +select dbms_assert.enquote_literal(NULL); + enquote_literal +----------------- + +(1 row) + +select dbms_assert.enquote_name(NULL); + enquote_name +-------------- + +(1 row) + +select dbms_assert.enquote_name(NULL, false); + enquote_name +-------------- + +(1 row) + +select dbms_assert.noop(NULL); + noop +------ + +(1 row) + +select dbms_assert.qualified_sql_name(NULL); +ERROR: string is not qualified SQL name +select dbms_assert.qualified_sql_name(NULL); +ERROR: string is not qualified SQL name +select dbms_assert.schema_name(NULL); +ERROR: invalid schema name +select dbms_assert.schema_name(NULL); +ERROR: invalid schema name +select dbms_assert.simple_sql_name(NULL); +ERROR: string is not simple SQL name +select dbms_assert.simple_sql_name(NULL); +ERROR: string is not simple SQL name +select dbms_assert.object_name(NULL); +ERROR: invalid object name +select dbms_assert.object_name(NULL); +ERROR: invalid object name +select plunit.assert_true(NULL); +ERROR: plunit.assert_true exception +DETAIL: Plunit.assertation fails (assert_true). +select plunit.assert_true(1 = 2); +ERROR: plunit.assert_true exception +DETAIL: Plunit.assertation fails (assert_true). +select plunit.assert_true(1 = 2, 'one is not two'); +ERROR: one is not two +DETAIL: Plunit.assertation fails (assert_true). +select plunit.assert_true(1 = 1); + assert_true +------------- + +(1 row) + +select plunit.assert_false(1 = 1); +ERROR: plunit.assert_false exception +DETAIL: Plunit.assertation fails (assert_false). +select plunit.assert_false(1 = 1, 'trap is open'); +ERROR: trap is open +DETAIL: Plunit.assertation fails (assert_false). +select plunit.assert_false(NULL); +ERROR: plunit.assert_false exception +DETAIL: Plunit.assertation fails (assert_false). +select plunit.assert_null(current_date); +ERROR: plunit.assert_null exception +DETAIL: Plunit.assertation fails (assert_null). +select plunit.assert_null(NULL::date); + assert_null +------------- + +(1 row) + +select plunit.assert_not_null(current_date); + assert_not_null +----------------- + +(1 row) + +select plunit.assert_not_null(NULL::date); +ERROR: plunit.assert_not_null exception +DETAIL: Plunit.assertation fails (assert_not_null). +select plunit.assert_equals('Pavel','Pa'||'vel'); + assert_equals +--------------- + +(1 row) + +select plunit.assert_equals(current_date, current_date + 1, 'diff dates'); +ERROR: diff dates +DETAIL: Plunit.assertation fails (assert_equals). +select plunit.assert_equals(10.2, 10.3, 0.5); + assert_equals +--------------- + +(1 row) + +select plunit.assert_equals(10.2, 10.3, 0.01, 'attention some diff'); +ERROR: attention some diff +DETAIL: Plunit.assertation fails (assert_equals). +select plunit.assert_not_equals(current_date, current_date + 1, 'yestarday is today'); + assert_not_equals +------------------- + +(1 row) + +select plunit.fail(); +ERROR: plunit.assert_fail exception +DETAIL: Plunit.assertation (assert_fail). +select plunit.fail('custom exception'); +ERROR: custom exception +DETAIL: Plunit.assertation (assert_fail). +SELECT dump('Yellow dog'::text) ~ E'^Typ=25 Len=(\\d+): \\d+(,\\d+)*$' AS t; + t +--- + t +(1 row) + +SELECT dump('Yellow dog'::text, 10) ~ E'^Typ=25 Len=(\\d+): \\d+(,\\d+)*$' AS t; + t +--- + t +(1 row) + +SELECT dump('Yellow dog'::text, 17) ~ E'^Typ=25 Len=(\\d+): .(,.)*$' AS t; + t +--- + t +(1 row) + +SELECT dump(10::int2) ~ E'^Typ=21 Len=2: \\d+(,\\d+){1}$' AS t; + t +--- + t +(1 row) + +SELECT dump(10::int4) ~ E'^Typ=23 Len=4: \\d+(,\\d+){3}$' AS t; + t +--- + t +(1 row) + +SELECT dump(10::int8) ~ E'^Typ=20 Len=8: \\d+(,\\d+){7}$' AS t; + t +--- + t +(1 row) + +SELECT dump(10.23::float4) ~ E'^Typ=700 Len=4: \\d+(,\\d+){3}$' AS t; + t +--- + t +(1 row) + +SELECT dump(10.23::float8) ~ E'^Typ=701 Len=8: \\d+(,\\d+){7}$' AS t; + t +--- + t +(1 row) + +SELECT dump(10.23::numeric) ~ E'^Typ=1700 Len=(\\d+): \\d+(,\\d+)*$' AS t; + t +--- + t +(1 row) + +SELECT dump('2008-10-10'::date) ~ E'^Typ=1082 Len=4: \\d+(,\\d+){3}$' AS t; + t +--- + t +(1 row) + +SELECT dump('2008-10-10'::timestamp) ~ E'^Typ=1114 Len=8: \\d+(,\\d+){7}$' AS t; + t +--- + t +(1 row) + +SELECT dump('2009-10-10'::timestamp) ~ E'^Typ=1114 Len=8: \\d+(,\\d+){7}$' AS t; + t +--- + t +(1 row) + +-- Tests for to_multi_byte +SELECT to_multi_byte('123$test'); + to_multi_byte +------------------ + 123$test +(1 row) + +-- Check internal representation difference +SELECT octet_length('abc'); + octet_length +-------------- + 3 +(1 row) + +SELECT octet_length(to_multi_byte('abc')); + octet_length +-------------- + 9 +(1 row) + +-- Tests for to_single_byte +SELECT to_single_byte('123$test'); + to_single_byte +---------------- + 123$test +(1 row) + +SELECT to_single_byte('123$test'); + to_single_byte +---------------- + 123$test +(1 row) + +-- Check internal representation difference +SELECT octet_length('abc'); + octet_length +-------------- + 9 +(1 row) + +SELECT octet_length(to_single_byte('abc')); + octet_length +-------------- + 3 +(1 row) + +-- Tests for round(TIMESTAMP WITH TIME ZONE) +select round(TIMESTAMP WITH TIME ZONE'12/08/1990 05:35:25','YEAR') = '1991-01-01 00:00:00'; + ?column? +---------- + t +(1 row) + +select round(TIMESTAMP WITH TIME ZONE'05/08/1990 05:35:25','Q') = '1990-04-01 00:00:00'; + ?column? +---------- + t +(1 row) + +select round(TIMESTAMP WITH TIME ZONE'12/08/1990 05:35:25','MONTH') = '1990-12-01 00:00:00'; + ?column? +---------- + t +(1 row) + +select round(TIMESTAMP WITH TIME ZONE'12/08/1990 05:35:25','DDD') = '1990-12-08 00:00:00'; + ?column? +---------- + t +(1 row) + +select round(TIMESTAMP WITH TIME ZONE'12/08/1990 05:35:25','DAY') = '1990-12-09 00:00:00'; + ?column? +---------- + t +(1 row) + +select round(TIMESTAMP WITH TIME ZONE'12/08/1990 05:35:25','hh') = '1990-12-08 06:00:00'; + ?column? +---------- + t +(1 row) + +select round(TIMESTAMP WITH TIME ZONE'12/08/1990 05:35:25','mi') = '1990-12-08 05:35:00'; + ?column? +---------- + t +(1 row) + +-- Tests for to_date +SET DATESTYLE TO SQL, MDY; +SELECT to_date('2009-01-02'); + to_date +--------------------- + 01/02/2009 00:00:00 +(1 row) + +select to_date('January 8,1999'); + to_date +--------------------- + 01/08/1999 00:00:00 +(1 row) + +SET DATESTYLE TO POSTGRES, MDY; +select to_date('1999-01-08'); + to_date +-------------------------- + Fri Jan 08 00:00:00 1999 +(1 row) + +select to_date('1/12/1999'); + to_date +-------------------------- + Tue Jan 12 00:00:00 1999 +(1 row) + +SET DATESTYLE TO SQL, DMY; +select to_date('01/02/03'); + to_date +--------------------- + 01/02/2003 00:00:00 +(1 row) + +select to_date('1999-Jan-08'); + to_date +--------------------- + 08/01/1999 00:00:00 +(1 row) + +select to_date('Jan-08-1999'); + to_date +--------------------- + 08/01/1999 00:00:00 +(1 row) + +select to_date('08-Jan-1999'); + to_date +--------------------- + 08/01/1999 00:00:00 +(1 row) + +SET DATESTYLE TO ISO, YMD; +select to_date('99-Jan-08'); + to_date +--------------------- + 1999-01-08 00:00:00 +(1 row) + +SET DATESTYLE TO ISO, DMY; +select to_date('08-Jan-99'); + to_date +--------------------- + 1999-01-08 00:00:00 +(1 row) + +select to_date('Jan-08-99'); + to_date +--------------------- + 1999-01-08 00:00:00 +(1 row) + +select to_date('19990108'); + to_date +--------------------- + 1999-01-08 00:00:00 +(1 row) + +select to_date('990108'); + to_date +--------------------- + 1999-01-08 00:00:00 +(1 row) + +select to_date('J2451187'); + to_date +--------------------- + 1999-01-08 00:00:00 +(1 row) + +set orafce.nls_date_format='YY-MonDD HH24:MI:SS'; +select to_date('14-Jan08 11:44:49+05:30'); + to_date +--------------------- + 2014-01-08 11:44:49 +(1 row) + +set orafce.nls_date_format='YY-DDMon HH24:MI:SS'; +select to_date('14-08Jan 11:44:49+05:30'); + to_date +--------------------- + 2014-01-08 11:44:49 +(1 row) + +set orafce.nls_date_format='DDMMYYYY HH24:MI:SS'; +select to_date('21052014 12:13:44+05:30'); + to_date +--------------------- + 2014-05-21 12:13:44 +(1 row) + +set orafce.nls_date_format='DDMMYY HH24:MI:SS'; +select to_date('210514 12:13:44+05:30'); + to_date +--------------------- + 2014-05-21 12:13:44 +(1 row) + +set orafce.nls_date_format='DDMMYY HH24:MI:SS.MS'; +select pg_catalog.to_date('210514 12:13:44.55'); + to_date +------------------------ + 2014-05-21 12:13:44.55 +(1 row) + +select oracle.to_date('210514 12:13:44.55'); + to_date +--------------------- + 2014-05-21 12:13:45 +(1 row) + +-- Tests for oracle.to_date(text,text) +SET search_path TO oracle,"$user", public, pg_catalog; +select to_date('2014/04/25 10:13', 'YYYY/MM/DD HH:MI'); + to_date +--------------------- + 2014-04-25 10:13:00 +(1 row) + +select to_date('16-Feb-09 10:11:11', 'DD-Mon-YY HH:MI:SS'); + to_date +--------------------- + 2009-02-16 10:11:11 +(1 row) + +select to_date('02/16/09 04:12:12', 'MM/DD/YY HH24:MI:SS'); + to_date +--------------------- + 2009-02-16 04:12:12 +(1 row) + +select to_date('021609 111213', 'MMDDYY HHMISS'); + to_date +--------------------- + 2009-02-16 11:12:13 +(1 row) + +select to_date('16-Feb-09 11:12:12', 'DD-Mon-YY HH:MI:SS'); + to_date +--------------------- + 2009-02-16 11:12:12 +(1 row) + +select to_date('Feb/16/09 11:21:23', 'Mon/DD/YY HH:MI:SS'); + to_date +--------------------- + 2009-02-16 11:21:23 +(1 row) + +select to_date('February.16.2009 10:11:12', 'Month.DD.YYYY HH:MI:SS'); + to_date +--------------------- + 2009-02-16 10:11:12 +(1 row) + +select to_date('20020315111212', 'yyyymmddhh12miss'); + to_date +--------------------- + 2002-03-15 11:12:12 +(1 row) + +select to_date('January 15, 1989, 11:00 A.M.','Month dd, YYYY, HH:MI A.M.'); + to_date +--------------------- + 1989-01-15 11:00:00 +(1 row) + +select to_date('14-Jan08 11:44:49+05:30' ,'YY-MonDD HH24:MI:SS'); + to_date +--------------------- + 2014-01-08 11:44:49 +(1 row) + +select to_date('14-08Jan 11:44:49+05:30','YY-DDMon HH24:MI:SS'); + to_date +--------------------- + 2014-01-08 11:44:49 +(1 row) + +select to_date('21052014 12:13:44+05:30','DDMMYYYY HH24:MI:SS'); + to_date +--------------------- + 2014-05-21 12:13:44 +(1 row) + +select to_date('210514 12:13:44+05:30','DDMMYY HH24:MI:SS'); + to_date +--------------------- + 2014-05-21 12:13:44 +(1 row) + +SET search_path TO default; +-- Tests for + operator with DATE and number(smallint,integer,bigint,numeric) +SET search_path TO oracle,"$user", public, pg_catalog; +SET orafce.nls_date_format='YYYY-MM-DD HH24:MI:SS'; +SELECT to_date('2014-07-02 10:08:55') + 9::smallint; + ?column? +--------------------- + 2014-07-11 10:08:55 +(1 row) + +SET orafce.nls_date_format='MM-DD-YYYY HH24:MI:SS'; +SELECT to_date('07-02-2014 10:08:55') + 9::smallint; + ?column? +--------------------- + 2014-07-11 10:08:55 +(1 row) + +SET orafce.nls_date_format='DD-MM-YYYY HH24:MI:SS'; +SELECT to_date('02-07-2014 10:08:55') + 9::smallint; + ?column? +--------------------- + 2014-07-11 10:08:55 +(1 row) + +SET orafce.nls_date_format='YYYY-MM-DD HH24:MI:SS'; +SELECT to_date('2014-07-02 10:08:55') + 9; + ?column? +--------------------- + 2014-07-11 10:08:55 +(1 row) + +SELECT to_date('2014-07-02 10:08:55','YYYY-MM-DD HH:MI:SS') + 9::smallint; + ?column? +--------------------- + 2014-07-11 10:08:55 +(1 row) + +SELECT to_date('02-07-2014 10:08:55','DD-MM-YYYY HH:MI:SS') + 9::smallint; + ?column? +--------------------- + 2014-07-11 10:08:55 +(1 row) + +SELECT to_date('07-02-2014 10:08:55','MM-DD-YYYY HH:MI:SS') + 9::smallint; + ?column? +--------------------- + 2014-07-11 10:08:55 +(1 row) + +SET orafce.nls_date_format='YYYY-MM-DD HH24:MI:SS'; +SELECT to_date('2014-07-02 10:08:55') + 9::bigint; + ?column? +--------------------- + 2014-07-11 10:08:55 +(1 row) + +SET orafce.nls_date_format='MM-DD-YYYY HH24:MI:SS'; +SELECT to_date('07-02-2014 10:08:55') + 9::bigint; + ?column? +--------------------- + 2014-07-11 10:08:55 +(1 row) + +SET orafce.nls_date_format='DD-MM-YYYY HH24:MI:SS'; +SELECT to_date('02-07-2014 10:08:55') + 9::bigint; + ?column? +--------------------- + 2014-07-11 10:08:55 +(1 row) + +SELECT to_date('2014-07-02 10:08:55','YYYY-MM-DD HH:MI:SS') + 9::bigint; + ?column? +--------------------- + 2014-07-11 10:08:55 +(1 row) + +SELECT to_date('02-07-2014 10:08:55','DD-MM-YYYY HH:MI:SS') + 9::bigint; + ?column? +--------------------- + 2014-07-11 10:08:55 +(1 row) + +SELECT to_date('07-02-2014 10:08:55','MM-DD-YYYY HH:MI:SS') + 9::bigint; + ?column? +--------------------- + 2014-07-11 10:08:55 +(1 row) + +SET orafce.nls_date_format='YYYY-MM-DD HH24:MI:SS'; +SELECT to_date('2014-07-02 10:08:55') + 9::integer; + ?column? +--------------------- + 2014-07-11 10:08:55 +(1 row) + +SET orafce.nls_date_format='MM-DD-YYYY HH24:MI:SS'; +SELECT to_date('07-02-2014 10:08:55') + 9::integer; + ?column? +--------------------- + 2014-07-11 10:08:55 +(1 row) + +SET orafce.nls_date_format='DD-MM-YYYY HH24:MI:SS'; +SELECT to_date('02-07-2014 10:08:55') + 9::integer; + ?column? +--------------------- + 2014-07-11 10:08:55 +(1 row) + +SELECT to_date('2014-07-02 10:08:55','YYYY-MM-DD HH:MI:SS') + 9::integer; + ?column? +--------------------- + 2014-07-11 10:08:55 +(1 row) + +SELECT to_date('02-07-2014 10:08:55','DD-MM-YYYY HH:MI:SS') + 9::integer; + ?column? +--------------------- + 2014-07-11 10:08:55 +(1 row) + +SELECT to_date('07-02-2014 10:08:55','MM-DD-YYYY HH:MI:SS') + 9::integer; + ?column? +--------------------- + 2014-07-11 10:08:55 +(1 row) + +SET orafce.nls_date_format='YYYY-MM-DD HH24:MI:SS'; +SELECT to_date('2014-07-02 10:08:55') + 9::numeric; + ?column? +--------------------- + 2014-07-11 10:08:55 +(1 row) + +SET orafce.nls_date_format='MM-DD-YYYY HH24:MI:SS'; +SELECT to_date('07-02-2014 10:08:55') + 9::numeric; + ?column? +--------------------- + 2014-07-11 10:08:55 +(1 row) + +SET orafce.nls_date_format='DD-MM-YYYY HH24:MI:SS'; +SELECT to_date('02-07-2014 10:08:55') + 9::numeric; + ?column? +--------------------- + 2014-07-11 10:08:55 +(1 row) + +SELECT to_date('2014-07-02 10:08:55','YYYY-MM-DD HH:MI:SS') + 9::numeric; + ?column? +--------------------- + 2014-07-11 10:08:55 +(1 row) + +SELECT to_date('02-07-2014 10:08:55','DD-MM-YYYY HH:MI:SS') + 9::numeric; + ?column? +--------------------- + 2014-07-11 10:08:55 +(1 row) + +SELECT to_date('07-02-2014 10:08:55','MM-DD-YYYY HH:MI:SS') + 9::numeric; + ?column? +--------------------- + 2014-07-11 10:08:55 +(1 row) + +SET orafce.nls_date_format='YYYY-MM-DD HH24:MI:SS'; +SELECT to_date('2014-01-01 00:00:00') + 1.5; + ?column? +--------------------- + 2014-01-02 12:00:00 +(1 row) + +SELECT to_date('2014-01-01 00:00:00','yyyy-mm-dd hh24:mi:ss') + 1.5; + ?column? +--------------------- + 2014-01-02 12:00:00 +(1 row) + +SET search_path TO default; +-- Tests for - operator with DATE and number(smallint,integer,bigint,numeric) +SET search_path TO oracle,"$user", public, pg_catalog; +SET orafce.nls_date_format='YYYY-MM-DD HH24:MI:SS'; +SELECT to_date('2014-07-02 10:08:55') - 9::smallint; + ?column? +--------------------- + 2014-06-23 10:08:55 +(1 row) + +SET orafce.nls_date_format='MM-DD-YYYY HH24:MI:SS'; +SELECT to_date('07-02-2014 10:08:55') - 9::smallint; + ?column? +--------------------- + 2014-06-23 10:08:55 +(1 row) + +SET orafce.nls_date_format='DD-MM-YYYY HH24:MI:SS'; +SELECT to_date('02-07-2014 10:08:55') - 9::smallint; + ?column? +--------------------- + 2014-06-23 10:08:55 +(1 row) + +SET orafce.nls_date_format='YYYY-MM-DD HH24:MI:SS'; +SELECT to_date('2014-07-02 10:08:55') - 9; + ?column? +--------------------- + 2014-06-23 10:08:55 +(1 row) + +SELECT to_date('2014-07-02 10:08:55','YYYY-MM-DD HH:MI:SS') - 9::smallint; + ?column? +--------------------- + 2014-06-23 10:08:55 +(1 row) + +SELECT to_date('02-07-2014 10:08:55','DD-MM-YYYY HH:MI:SS') - 9::smallint; + ?column? +--------------------- + 2014-06-23 10:08:55 +(1 row) + +SELECT to_date('07-02-2014 10:08:55','MM-DD-YYYY HH:MI:SS') - 9::smallint; + ?column? +--------------------- + 2014-06-23 10:08:55 +(1 row) + +SET orafce.nls_date_format='YYYY-MM-DD HH24:MI:SS'; +SELECT to_date('2014-07-02 10:08:55') - 9::bigint; + ?column? +--------------------- + 2014-06-23 10:08:55 +(1 row) + +SET orafce.nls_date_format='MM-DD-YYYY HH24:MI:SS'; +SELECT to_date('07-02-2014 10:08:55') - 9::bigint; + ?column? +--------------------- + 2014-06-23 10:08:55 +(1 row) + +SET orafce.nls_date_format='DD-MM-YYYY HH24:MI:SS'; +SELECT to_date('02-07-2014 10:08:55') - 9::bigint; + ?column? +--------------------- + 2014-06-23 10:08:55 +(1 row) + +SELECT to_date('2014-07-02 10:08:55','YYYY-MM-DD HH:MI:SS') - 9::bigint; + ?column? +--------------------- + 2014-06-23 10:08:55 +(1 row) + +SELECT to_date('02-07-2014 10:08:55','DD-MM-YYYY HH:MI:SS') - 9::bigint; + ?column? +--------------------- + 2014-06-23 10:08:55 +(1 row) + +SELECT to_date('07-02-2014 10:08:55','MM-DD-YYYY HH:MI:SS') - 9::bigint; + ?column? +--------------------- + 2014-06-23 10:08:55 +(1 row) + +SET orafce.nls_date_format='YYYY-MM-DD HH24:MI:SS'; +SELECT to_date('2014-07-02 10:08:55') - 9::integer; + ?column? +--------------------- + 2014-06-23 10:08:55 +(1 row) + +SET orafce.nls_date_format='MM-DD-YYYY HH24:MI:SS'; +SELECT to_date('07-02-2014 10:08:55') - 9::integer; + ?column? +--------------------- + 2014-06-23 10:08:55 +(1 row) + +SET orafce.nls_date_format='DD-MM-YYYY HH24:MI:SS'; +SELECT to_date('02-07-2014 10:08:55') - 9::integer; + ?column? +--------------------- + 2014-06-23 10:08:55 +(1 row) + +SELECT to_date('2014-07-02 10:08:55','YYYY-MM-DD HH:MI:SS') - 9::integer; + ?column? +--------------------- + 2014-06-23 10:08:55 +(1 row) + +SELECT to_date('02-07-2014 10:08:55','DD-MM-YYYY HH:MI:SS') - 9::integer; + ?column? +--------------------- + 2014-06-23 10:08:55 +(1 row) + +SELECT to_date('07-02-2014 10:08:55','MM-DD-YYYY HH:MI:SS') - 9::integer; + ?column? +--------------------- + 2014-06-23 10:08:55 +(1 row) + +SET orafce.nls_date_format='YYYY-MM-DD HH24:MI:SS'; +SELECT to_date('2014-07-02 10:08:55') - 9::numeric; + ?column? +--------------------- + 2014-06-23 10:08:55 +(1 row) + +SET orafce.nls_date_format='MM-DD-YYYY HH24:MI:SS'; +SELECT to_date('07-02-2014 10:08:55') - 9::numeric; + ?column? +--------------------- + 2014-06-23 10:08:55 +(1 row) + +SET orafce.nls_date_format='DD-MM-YYYY HH24:MI:SS'; +SELECT to_date('02-07-2014 10:08:55') - 9::numeric; + ?column? +--------------------- + 2014-06-23 10:08:55 +(1 row) + +SELECT to_date('2014-07-02 10:08:55','YYYY-MM-DD HH:MI:SS') - 9::numeric; + ?column? +--------------------- + 2014-06-23 10:08:55 +(1 row) + +SELECT to_date('02-07-2014 10:08:55','DD-MM-YYYY HH:MI:SS') - 9::numeric; + ?column? +--------------------- + 2014-06-23 10:08:55 +(1 row) + +SELECT to_date('07-02-2014 10:08:55','MM-DD-YYYY HH:MI:SS') - 9::numeric; + ?column? +--------------------- + 2014-06-23 10:08:55 +(1 row) + +SET orafce.nls_date_format='YYYY-MM-DD HH24:MI:SS'; +SELECT to_date('2014-01-01 00:00:00') - 1.5; + ?column? +--------------------- + 2013-12-30 12:00:00 +(1 row) + +SELECT to_date('2014-01-01 00:00:00','yyyy-mm-dd hh24:mi:ss') - 1.5; + ?column? +--------------------- + 2013-12-30 12:00:00 +(1 row) + +SET search_path TO default; +--Tests for oracle.to_char(timestamp)-used to set the DATE output format +SET search_path TO oracle,"$user", public, pg_catalog; +SET orafce.nls_date_format to default; +select oracle.to_char(to_date('19-APR-16 21:41:48')); + to_char +--------------------- + 2016-04-19 21:41:48 +(1 row) + +set orafce.nls_date_format='YY-MonDD HH24:MI:SS'; +select oracle.to_char(to_date('14-Jan08 11:44:49+05:30')); + to_char +------------------- + 14-Jan08 11:44:49 +(1 row) + +set orafce.nls_date_format='YY-DDMon HH24:MI:SS'; +select oracle.to_char(to_date('14-08Jan 11:44:49+05:30')); + to_char +------------------- + 14-08Jan 11:44:49 +(1 row) + +set orafce.nls_date_format='DDMMYYYY HH24:MI:SS'; +select oracle.to_char(to_date('21052014 12:13:44+05:30')); + to_char +------------------- + 21052014 12:13:44 +(1 row) + +set orafce.nls_date_format='DDMMYY HH24:MI:SS'; +select oracle.to_char(to_date('210514 12:13:44+05:30')); + to_char +----------------- + 210514 12:13:44 +(1 row) + +set orafce.nls_date_format='DDMMYYYY HH24:MI:SS'; +select oracle.to_char(oracle.to_date('2014/04/25 10:13', 'YYYY/MM/DD HH:MI')); + to_char +------------------- + 25042014 10:13:00 +(1 row) + +set orafce.nls_date_format='YY-DDMon HH24:MI:SS'; +select oracle.to_char(oracle.to_date('16-Feb-09 10:11:11', 'DD-Mon-YY HH:MI:SS')); + to_char +------------------- + 09-16Feb 10:11:11 +(1 row) + +set orafce.nls_date_format='YY-DDMon HH24:MI:SS'; +select oracle.to_char(oracle.to_date('02/16/09 04:12:12', 'MM/DD/YY HH24:MI:SS')); + to_char +------------------- + 09-16Feb 04:12:12 +(1 row) + +set orafce.nls_date_format='YY-MonDD HH24:MI:SS'; +select oracle.to_char(oracle.to_date('021609 111213', 'MMDDYY HHMISS')); + to_char +------------------- + 09-Feb16 11:12:13 +(1 row) + +set orafce.nls_date_format='DDMMYYYY HH24:MI:SS'; +select oracle.to_char(oracle.to_date('16-Feb-09 11:12:12', 'DD-Mon-YY HH:MI:SS')); + to_char +------------------- + 16022009 11:12:12 +(1 row) + +set orafce.nls_date_format='DDMMYYYY HH24:MI:SS'; +select oracle.to_char(oracle.to_date('Feb/16/09 11:21:23', 'Mon/DD/YY HH:MI:SS')); + to_char +------------------- + 16022009 11:21:23 +(1 row) + +set orafce.nls_date_format='DDMMYYYY HH24:MI:SS'; +select oracle.to_char(oracle.to_date('February.16.2009 10:11:12', 'Month.DD.YYYY HH:MI:SS')); + to_char +------------------- + 16022009 10:11:12 +(1 row) + +set orafce.nls_date_format='YY-MonDD HH24:MI:SS'; +select oracle.to_char(oracle.to_date('20020315111212', 'yyyymmddhh12miss')); + to_char +------------------- + 02-Mar15 11:12:12 +(1 row) + +set orafce.nls_date_format='DDMMYYYY HH24:MI:SS'; +select oracle.to_char(oracle.to_date('January 15, 1989, 11:00 A.M.','Month dd, YYYY, HH:MI A.M.')); + to_char +------------------- + 15011989 11:00:00 +(1 row) + +set orafce.nls_date_format='DDMMYY HH24:MI:SS'; +select oracle.to_char(oracle.to_date('14-Jan08 11:44:49+05:30' ,'YY-MonDD HH24:MI:SS')); + to_char +----------------- + 080114 11:44:49 +(1 row) + +set orafce.nls_date_format='DDMMYYYY HH24:MI:SS'; +select oracle.to_char(oracle.to_date('14-08Jan 11:44:49+05:30','YY-DDMon HH24:MI:SS')); + to_char +------------------- + 08012014 11:44:49 +(1 row) + +set orafce.nls_date_format='YY-MonDD HH24:MI:SS'; +select oracle.to_char(oracle.to_date('21052014 12:13:44+05:30','DDMMYYYY HH24:MI:SS')); + to_char +------------------- + 14-May21 12:13:44 +(1 row) + +set orafce.nls_date_format='DDMMYY HH24:MI:SS'; +select oracle.to_char(oracle.to_date('210514 12:13:44+05:30','DDMMYY HH24:MI:SS')); + to_char +----------------- + 210514 12:13:44 +(1 row) + +SET search_path TO default; +--Tests for oracle.-(oracle.date,oracle.date) +SET search_path TO oracle,"$user", public, pg_catalog; +SELECT (to_date('2014-07-17 11:10:15', 'yyyy-mm-dd hh24:mi:ss') - to_date('2014-02-01 10:00:00', 'yyyy-mm-dd hh24:mi:ss'))::numeric(10,4); + numeric +---------- + 166.0488 +(1 row) + +SELECT (to_date('2014-07-17 13:14:15', 'yyyy-mm-dd hh24:mi:ss') - to_date('2014-02-01 10:00:00', 'yyyy-mm-dd hh24:mi:ss'))::numeric(10,4); + numeric +---------- + 166.1349 +(1 row) + +SELECT (to_date('07-17-2014 13:14:15', 'mm-dd-yyyy hh24:mi:ss') - to_date('2014-02-01 10:00:00', 'yyyy-mm-dd hh24:mi:ss'))::numeric(10,4); + numeric +---------- + 166.1349 +(1 row) + +SELECT (to_date('07-17-2014 13:14:15', 'mm-dd-yyyy hh24:mi:ss') - to_date('2015-02-01 10:00:00', 'yyyy-mm-dd hh24:mi:ss'))::numeric(10,4); + numeric +----------- + -198.8651 +(1 row) + +SELECT (to_date('07-17-2014 13:14:15', 'mm-dd-yyyy hh24:mi:ss') - to_date('01-01-2013 10:00:00', 'mm-dd-yyyy hh24:mi:ss'))::numeric(10,4); + numeric +---------- + 562.1349 +(1 row) + +SELECT (to_date('17-07-2014 13:14:15', 'dd-mm-yyyy hh24:mi:ss') - to_date('01-01-2013 10:00:00', 'dd--mm-yyyy hh24:mi:ss'))::numeric(10,4); + numeric +---------- + 562.1349 +(1 row) + +SELECT (to_date('2014/02/01 10:11:12', 'YYYY/MM/DD hh12:mi:ss') - to_date('2013/02/01 10:11:12', 'YYYY/MM/DD hh12:mi:ss'))::numeric(10,4); + numeric +---------- + 365.0000 +(1 row) + +SELECT (to_date('17-Jul-14 10:11:11', 'DD-Mon-YY HH:MI:SS') - to_date('17-Jan-14 00:00:00', 'DD-Mon-YY HH24:MI:SS'))::numeric(10,4); + numeric +---------- + 181.4244 +(1 row) + +SELECT (to_date('July.17.2014 10:11:12', 'Month.DD.YYYY HH:MI:SS') - to_date('February.16.2014 10:21:12', 'Month.DD.YYYY HH:MI:SS'))::numeric(10,4); + numeric +---------- + 150.9931 +(1 row) + +SELECT (to_date('20140717111211', 'yyyymmddhh12miss') - to_date('20140315111212', 'yyyymmddhh12miss'))::numeric(10,4); + numeric +---------- + 124.0000 +(1 row) + +SELECT (to_date('January 15, 1990, 11:00 A.M.','Month dd, YYYY, HH:MI A.M.') - to_date('January 15, 1989, 10:00 A.M.','Month dd, YYYY, HH:MI A.M.'))::numeric(10,4); + numeric +---------- + 365.0417 +(1 row) + +SELECT (to_date('14-Jul14 11:44:49' ,'YY-MonDD HH24:MI:SS') - to_date('14-Jan14 12:44:49' ,'YY-MonDD HH24:MI:SS'))::numeric(10,4); + numeric +---------- + 180.9583 +(1 row) + +SELECT (to_date('210514 12:13:44','DDMMYY HH24:MI:SS') - to_date('210114 10:13:44','DDMMYY HH24:MI:SS'))::numeric(10,4); + numeric +---------- + 120.0833 +(1 row) + +SELECT trunc(to_date('210514 12:13:44','DDMMYY HH24:MI:SS')); + trunc +--------------------- + 2014-05-21 00:00:00 +(1 row) + +SELECT round(to_date('210514 12:13:44','DDMMYY HH24:MI:SS')); + round +--------------------- + 2014-05-22 00:00:00 +(1 row) + +SET search_path TO default; +-- +-- Note: each Japanese character used below has display width of 2, otherwise 1. +-- Note: each output string is surrounded by '|' for improved readability +-- +-- +-- test LPAD family of functions +-- +/* cases where one or more arguments are of type CHAR */ +SELECT '|' || oracle.lpad('あbcd'::char(8), 10) || '|'; + ?column? +-------------- + | あbcd | +(1 row) + +SELECT '|' || oracle.lpad('あbcd'::char(8), 5) || '|'; + ?column? +---------- + |あbcd| +(1 row) + +SELECT '|' || oracle.lpad('あbcd'::char(8), 1) || '|'; + ?column? +---------- + | | +(1 row) + +SELECT '|' || oracle.lpad('あbcd'::char(5), 10, 'xい'::char(3)) || '|'; + ?column? +-------------- + |xい あbcd | +(1 row) + +SELECT '|' || oracle.lpad('あbcd'::char(5), 5, 'xい'::char(3)) || '|'; + ?column? +---------- + |あbcd| +(1 row) + +SELECT '|' || oracle.lpad('あbcd'::char(5), 10, 'xい'::text) || '|'; + ?column? +-------------- + |xいxあbcd | +(1 row) + +SELECT '|' || oracle.lpad('あbcd'::char(5), 10, 'xい'::varchar2(5)) || '|'; + ?column? +-------------- + |xいxあbcd | +(1 row) + +SELECT '|' || oracle.lpad('あbcd'::char(5), 10, 'xい'::nvarchar2(3)) || '|'; + ?column? +-------------- + |xいxあbcd | +(1 row) + +SELECT '|' || oracle.lpad('あbcd'::text, 10, 'xい'::char(3)) || '|'; + ?column? +-------------- + |xい xあbcd| +(1 row) + +SELECT '|' || oracle.lpad('あbcd'::text, 5, 'xい'::char(3)) || '|'; + ?column? +---------- + |あbcd| +(1 row) + +SELECT '|' || oracle.lpad('あbcd'::varchar2(5), 10, 'xい'::char(3)) || '|'; + ?column? +-------------- + | xい xあbc| +(1 row) + +SELECT '|' || oracle.lpad('あbcd'::varchar2(5), 5, 'xい'::char(3)) || '|'; + ?column? +---------- + |xあbc| +(1 row) + +SELECT '|' || oracle.lpad('あbcd'::nvarchar2(5), 10, 'xい'::char(3)) || '|'; + ?column? +-------------- + |xい xあbcd| +(1 row) + +SELECT '|' || oracle.lpad('あbcd'::nvarchar2(5), 5, 'xい'::char(3)) || '|'; + ?column? +---------- + |あbcd| +(1 row) + +/* test oracle.lpad(text, int [, text]) */ +SELECT '|' || oracle.lpad('あbcd'::text, 10) || '|'; + ?column? +-------------- + | あbcd| +(1 row) + +SELECT '|' || oracle.lpad('あbcd'::text, 5) || '|'; + ?column? +---------- + |あbcd| +(1 row) + +SELECT '|' || oracle.lpad('あbcd'::varchar2(10), 10) || '|'; + ?column? +-------------- + | あbcd| +(1 row) + +SELECT '|' || oracle.lpad('あbcd'::varchar2(10), 5) || '|'; + ?column? +---------- + |あbcd| +(1 row) + +SELECT '|' || oracle.lpad('あbcd'::nvarchar2(10), 10) || '|'; + ?column? +-------------- + | あbcd| +(1 row) + +SELECT '|' || oracle.lpad('あbcd'::nvarchar2(10), 5) || '|'; + ?column? +---------- + |あbcd| +(1 row) + +SELECT '|' || oracle.lpad('あbcd'::text, 10, 'xい'::text) || '|'; + ?column? +-------------- + | xいxあbcd| +(1 row) + +SELECT '|' || oracle.lpad('あbcd'::text, 10, 'xい'::varchar2(5)) || '|'; + ?column? +-------------- + | xいxあbcd| +(1 row) + +SELECT '|' || oracle.lpad('あbcd'::text, 10, 'xい'::nvarchar2(3)) || '|'; + ?column? +-------------- + | xいxあbcd| +(1 row) + +SELECT '|' || oracle.lpad('あbcd'::varchar2(5), 10, 'xい'::text) || '|'; + ?column? +-------------- + |xいxいあbc| +(1 row) + +SELECT '|' || oracle.lpad('あbcd'::varchar2(5), 10, 'xい'::varchar2(5)) || '|'; + ?column? +-------------- + |xいxいあbc| +(1 row) + +SELECT '|' || oracle.lpad('あbcd'::varchar2(5), 10, 'xい'::nvarchar2(5)) || '|'; + ?column? +-------------- + |xいxいあbc| +(1 row) + +SELECT '|' || oracle.lpad('あbcd'::nvarchar2(5), 10, 'xい'::text) || '|'; + ?column? +-------------- + | xいxあbcd| +(1 row) + +SELECT '|' || oracle.lpad('あbcd'::nvarchar2(5), 10, 'xい'::varchar2(5)) || '|'; + ?column? +-------------- + | xいxあbcd| +(1 row) + +SELECT '|' || oracle.lpad('あbcd'::nvarchar2(5), 10, 'xい'::nvarchar2(5)) || '|'; + ?column? +-------------- + | xいxあbcd| +(1 row) + +-- +-- test RPAD family of functions +-- +/* cases where one or more arguments are of type CHAR */ +SELECT '|' || oracle.rpad('あbcd'::char(8), 10) || '|'; + ?column? +-------------- + |あbcd | +(1 row) + +SELECT '|' || oracle.rpad('あbcd'::char(8), 5) || '|'; + ?column? +---------- + |あbcd| +(1 row) + +SELECT '|' || oracle.rpad('あbcd'::char(8), 1) || '|'; + ?column? +---------- + | | +(1 row) + +SELECT '|' || oracle.rpad('あbcd'::char(5), 10, 'xい'::char(3)) || '|'; + ?column? +-------------- + |あbcd xい | +(1 row) + +SELECT '|' || oracle.rpad('あbcd'::char(5), 5, 'xい'::char(3)) || '|'; + ?column? +---------- + |あbcd| +(1 row) + +SELECT '|' || oracle.rpad('あbcd'::char(5), 10, 'xい'::text) || '|'; + ?column? +-------------- + |あbcd xいx| +(1 row) + +SELECT '|' || oracle.rpad('あbcd'::char(5), 10, 'xい'::varchar2(5)) || '|'; + ?column? +-------------- + |あbcd xいx| +(1 row) + +SELECT '|' || oracle.rpad('あbcd'::char(5), 10, 'xい'::nvarchar2(3)) || '|'; + ?column? +-------------- + |あbcd xいx| +(1 row) + +SELECT '|' || oracle.rpad('あbcd'::text, 10, 'xい'::char(3)) || '|'; + ?column? +-------------- + |あbcdxい x| +(1 row) + +SELECT '|' || oracle.rpad('あbcd'::text, 5, 'xい'::char(3)) || '|'; + ?column? +---------- + |あbcd| +(1 row) + +SELECT '|' || oracle.rpad('あbcd'::varchar2(5), 10, 'xい'::char(3)) || '|'; + ?column? +-------------- + |あbcxい x | +(1 row) + +SELECT '|' || oracle.rpad('あbcd'::varchar2(5), 5, 'xい'::char(3)) || '|'; + ?column? +---------- + |あbcx| +(1 row) + +SELECT '|' || oracle.rpad('あbcd'::nvarchar2(5), 10, 'xい'::char(3)) || '|'; + ?column? +-------------- + |あbcdxい x| +(1 row) + +SELECT '|' || oracle.rpad('あbcd'::nvarchar2(5), 5, 'xい'::char(3)) || '|'; + ?column? +---------- + |あbcd| +(1 row) + +/* test oracle.lpad(text, int [, text]) */ +SELECT '|' || oracle.rpad('あbcd'::text, 10) || '|'; + ?column? +-------------- + |あbcd | +(1 row) + +SELECT '|' || oracle.rpad('あbcd'::text, 5) || '|'; + ?column? +---------- + |あbcd| +(1 row) + +SELECT '|' || oracle.rpad('あbcd'::varchar2(10), 10) || '|'; + ?column? +-------------- + |あbcd | +(1 row) + +SELECT '|' || oracle.rpad('あbcd'::varchar2(10), 5) || '|'; + ?column? +---------- + |あbcd| +(1 row) + +SELECT '|' || oracle.rpad('あbcd'::nvarchar2(10), 10) || '|'; + ?column? +-------------- + |あbcd | +(1 row) + +SELECT '|' || oracle.rpad('あbcd'::nvarchar2(10), 5) || '|'; + ?column? +---------- + |あbcd| +(1 row) + +SELECT '|' || oracle.rpad('あbcd'::text, 10, 'xい'::text) || '|'; + ?column? +-------------- + |あbcdxいx | +(1 row) + +SELECT '|' || oracle.rpad('あbcd'::text, 10, 'xい'::varchar2(5)) || '|'; + ?column? +-------------- + |あbcdxいx | +(1 row) + +SELECT '|' || oracle.rpad('あbcd'::text, 10, 'xい'::nvarchar2(3)) || '|'; + ?column? +-------------- + |あbcdxいx | +(1 row) + +SELECT '|' || oracle.rpad('あbcd'::varchar2(5), 10, 'xい'::text) || '|'; + ?column? +-------------- + |あbcxいxい| +(1 row) + +SELECT '|' || oracle.rpad('あbcd'::varchar2(5), 10, 'xい'::varchar2(5)) || '|'; + ?column? +-------------- + |あbcxいxい| +(1 row) + +SELECT '|' || oracle.rpad('あbcd'::varchar2(5), 10, 'xい'::nvarchar2(5)) || '|'; + ?column? +-------------- + |あbcxいxい| +(1 row) + +SELECT '|' || oracle.rpad('あbcd'::nvarchar2(5), 10, 'xい'::text) || '|'; + ?column? +-------------- + |あbcdxいx | +(1 row) + +SELECT '|' || oracle.rpad('あbcd'::nvarchar2(5), 10, 'xい'::varchar2(5)) || '|'; + ?column? +-------------- + |あbcdxいx | +(1 row) + +SELECT '|' || oracle.rpad('あbcd'::nvarchar2(5), 10, 'xい'::nvarchar2(5)) || '|'; + ?column? +-------------- + |あbcdxいx | +(1 row) + +-- +-- test TRIM family of functions +-- +/* test that trailing blanks of CHAR arguments are not removed and are significant */ +-- +-- LTRIM +-- +SELECT '|' || oracle.ltrim(' abcd'::char(10)) || '|' as LTRIM; + ltrim +------------- + |abcd | +(1 row) + +SELECT '|' || oracle.ltrim(' abcd'::char(10),'a'::char(3)) || '|' as LTRIM; + ltrim +------------ + |bcd | +(1 row) + +SELECT '|' || oracle.ltrim(' abcd'::char(10),'a'::text) || '|' as LTRIM; + ltrim +-------------- + | abcd | +(1 row) + +SELECT '|' || oracle.ltrim(' abcd'::char(10),'a'::varchar2(3)) || '|' as LTRIM; + ltrim +-------------- + | abcd | +(1 row) + +SELECT '|' || oracle.ltrim(' abcd'::char(10),'a'::nvarchar2(3)) || '|' as LTRIM; + ltrim +-------------- + | abcd | +(1 row) + +SELECT '|' || oracle.ltrim(' abcd '::text,'a'::char(3)) || '|' as LTRIM; + ltrim +--------- + |bcd | +(1 row) + +SELECT '|' || oracle.ltrim(' abcd '::varchar2(10),'a'::char(3)) || '|' as LTRIM; + ltrim +--------- + |bcd | +(1 row) + +SELECT '|' || oracle.ltrim(' abcd '::nvarchar2(10),'a'::char(3)) || '|' as LTRIM; + ltrim +--------- + |bcd | +(1 row) + +-- +-- RTRIM +-- +SELECT '|' || oracle.rtrim(' abcd'::char(10)) || '|' as LTRIM; + ltrim +--------- + | abcd| +(1 row) + +SELECT '|' || oracle.rtrim(' abcd'::char(10),'d'::char(3)) || '|' as LTRIM; + ltrim +-------- + | abc| +(1 row) + +SELECT '|' || oracle.rtrim(' abcd'::char(10),'d'::text) || '|' as LTRIM; + ltrim +-------------- + | abcd | +(1 row) + +SELECT '|' || oracle.rtrim(' abcd'::char(10),'d'::varchar2(3)) || '|' as LTRIM; + ltrim +-------------- + | abcd | +(1 row) + +SELECT '|' || oracle.rtrim(' abcd'::char(10),'d'::nvarchar2(3)) || '|' as LTRIM; + ltrim +-------------- + | abcd | +(1 row) + +SELECT '|' || oracle.rtrim(' abcd '::text,'d'::char(3)) || '|' as LTRIM; + ltrim +-------- + | abc| +(1 row) + +SELECT '|' || oracle.rtrim(' abcd '::varchar2(10),'d'::char(3)) || '|' as LTRIM; + ltrim +-------- + | abc| +(1 row) + +SELECT '|' || oracle.rtrim(' abcd '::nvarchar2(10),'d'::char(3)) || '|' as LTRIM; + ltrim +-------- + | abc| +(1 row) + +-- +-- BTRIM +-- +SELECT '|' || oracle.btrim(' abcd'::char(10)) || '|' as LTRIM; + ltrim +-------- + |abcd| +(1 row) + +SELECT '|' || oracle.btrim(' abcd'::char(10),'ad'::char(3)) || '|' as LTRIM; + ltrim +------- + |bc| +(1 row) + +SELECT '|' || oracle.btrim(' abcd'::char(10),'ad'::text) || '|' as LTRIM; + ltrim +-------------- + | abcd | +(1 row) + +SELECT '|' || oracle.btrim(' abcd'::char(10),'ad'::varchar2(3)) || '|' as LTRIM; + ltrim +-------------- + | abcd | +(1 row) + +SELECT '|' || oracle.btrim(' abcd'::char(10),'ad'::nvarchar2(3)) || '|' as LTRIM; + ltrim +-------------- + | abcd | +(1 row) + +SELECT '|' || oracle.btrim(' abcd '::text,'d'::char(3)) || '|' as LTRIM; + ltrim +------- + |abc| +(1 row) + +SELECT '|' || oracle.btrim(' abcd '::varchar2(10),'d'::char(3)) || '|' as LTRIM; + ltrim +------- + |abc| +(1 row) + +SELECT '|' || oracle.btrim(' abcd '::nvarchar2(10),'d'::char(3)) || '|' as LTRIM; + ltrim +------- + |abc| +(1 row) + +-- +-- test oracle.length() +-- +/* test that trailing blanks are not ignored */ +SELECT oracle.length('あbb'::char(6)); + length +-------- + 6 +(1 row) + +SELECT oracle.length(''::char(6)); + length +-------- + 6 +(1 row) + +-- +-- test plvdate.bizdays_between +-- +SELECT plvdate.including_start(); + including_start +----------------- + t +(1 row) + +SELECT plvdate.bizdays_between('2016-02-24','2016-02-26'); + bizdays_between +----------------- + 3 +(1 row) + +SELECT plvdate.bizdays_between('2016-02-21','2016-02-27'); + bizdays_between +----------------- + 5 +(1 row) + +SELECT plvdate.include_start(false); + include_start +--------------- + +(1 row) + +SELECT plvdate.bizdays_between('2016-02-24','2016-02-26'); + bizdays_between +----------------- + 2 +(1 row) + +SELECT plvdate.bizdays_between('2016-02-21','2016-02-27'); + bizdays_between +----------------- + 5 +(1 row) + +SELECT oracle.round(1.234::double precision, 2), oracle.trunc(1.234::double precision, 2); + round | trunc +-------+------- + 1.23 | 1.23 +(1 row) + +SELECT oracle.round(1.234::float, 2), oracle.trunc(1.234::float, 2); + round | trunc +-------+------- + 1.23 | 1.23 +(1 row) + +-- +-- should not fail - fix: Crashes due to insufficent argument checking (#59) +-- +select dbms_random.string(null, 42); +ERROR: an argument is NULL +select dbms_pipe.create_pipe(null); +ERROR: pipe name is NULL +DETAIL: Pipename may not be NULL. +select plunit.assert_not_equals(1,2,3); +ERROR: plunit.assert_not_equal exception +DETAIL: Plunit.assertation fails (assert_not_equals). +-- +-- lexer text +-- +SELECT pos, token, class, mod FROM plvlex.tokens('select * from a.b.c join d on x=y', true, true); + pos | token | class | mod +-----+--------+---------+------ + 0 | select | KEYWORD | + 7 | * | OTHERS | self + 9 | from | KEYWORD | + 14 | a.b.c | IDENT | + 20 | join | KEYWORD | + 25 | d | IDENT | + 27 | on | KEYWORD | + 30 | x | IDENT | + 31 | = | OTHERS | self + 32 | y | IDENT | +(10 rows) + +-- +-- trigger functions +-- +CREATE TABLE trg_test(a varchar, b int, c varchar, d date, e int); +CREATE TRIGGER trg_test_xx BEFORE INSERT OR UPDATE + ON trg_test FOR EACH ROW EXECUTE PROCEDURE oracle.replace_empty_strings(true); +\pset null *** +INSERT INTO trg_test VALUES('',10, 'AHOJ', NULL, NULL); +WARNING: Field "a" of table "trg_test" is empty string (replaced by NULL). +INSERT INTO trg_test VALUES('AHOJ', NULL, '', '2020-01-01', 100); +WARNING: Field "c" of table "trg_test" is empty string (replaced by NULL). +SELECT * FROM trg_test order by a asc; + a | b | c | d | e +------+-----+------+------------+----- + AHOJ | *** | *** | 2020-01-01 | 100 + *** | 10 | AHOJ | *** | *** +(2 rows) + +DELETE FROM trg_test; +DROP TRIGGER trg_test_xx ON trg_test; +CREATE TRIGGER trg_test_xx BEFORE INSERT OR UPDATE + ON trg_test FOR EACH ROW EXECUTE PROCEDURE oracle.replace_null_strings(); +INSERT INTO trg_test VALUES(NULL, 10, 'AHOJ', NULL, NULL); +INSERT INTO trg_test VALUES('AHOJ', NULL, NULL, '2020-01-01', 100); +SELECT * FROM trg_test order by a asc; + a | b | c | d | e +------+-----+------+------------+----- + | 10 | AHOJ | *** | *** + AHOJ | *** | | 2020-01-01 | 100 +(2 rows) + +DROP TABLE trg_test; +SELECT oracle.unistr('\0441\043B\043E\043D'); + unistr +-------- + слон +(1 row) + +SELECT oracle.unistr('d\u0061t\U00000061'); + unistr +-------- + data +(1 row) + +-- run-time error +SELECT oracle.unistr('wrong: \db99'); +ERROR: invalid Unicode surrogate pair +SELECT oracle.unistr('wrong: \db99\0061'); +ERROR: invalid Unicode surrogate pair +SELECT oracle.unistr('wrong: \+00db99\+000061'); +ERROR: invalid Unicode surrogate pair +SELECT oracle.unistr('wrong: \+2FFFFF'); +ERROR: invalid Unicode escape value +SELECT oracle.unistr('wrong: \udb99\u0061'); +ERROR: invalid Unicode surrogate pair +SELECT oracle.unistr('wrong: \U0000db99\U00000061'); +ERROR: invalid Unicode surrogate pair +SELECT oracle.unistr('wrong: \U002FFFFF'); +ERROR: invalid Unicode escape value diff --git a/src/postgres/src/test/regress/expected/yb_pg_orafce2.out b/src/postgres/src/test/regress/expected/yb_pg_orafce2.out new file mode 100644 index 000000000000..70b36ce3b801 --- /dev/null +++ b/src/postgres/src/test/regress/expected/yb_pg_orafce2.out @@ -0,0 +1,4 @@ +-- 2) fails and throws error: 'ERROR: could not determine polymorphic type +-- because input has type "unknown"' +select decode('2012-01-01', '2012-01-01', 23, '2012-01-02', 24); +ERROR: could not determine polymorphic type because input has type unknown diff --git a/src/postgres/src/test/regress/expected/yb_pg_orafce_aggregates.out b/src/postgres/src/test/regress/expected/yb_pg_orafce_aggregates.out new file mode 100644 index 000000000000..1a4eb703ba47 --- /dev/null +++ b/src/postgres/src/test/regress/expected/yb_pg_orafce_aggregates.out @@ -0,0 +1,126 @@ +-- Tests for the aggregate listagg +SELECT listagg(i::text) from generate_series(1,3) g(i); + listagg +--------- + 123 +(1 row) + +SELECT listagg(i::text, ',') from generate_series(1,3) g(i); + listagg +--------- + 1,2,3 +(1 row) + +SELECT coalesce(listagg(i::text), '') from (SELECT ''::text) g(i); + coalesce +---------- + +(1 row) + +SELECT coalesce(listagg(i::text), '') from generate_series(1,0) g(i); + coalesce +---------- + +(1 row) + +SELECT wm_concat(i::text) from generate_series(1,3) g(i); + wm_concat +----------- + 1,2,3 +(1 row) + +-- Tests for the aggregate median( real | double ) +CREATE FUNCTION checkMedianRealOdd() RETURNS real AS $$ +DECLARE + med real; + +BEGIN + CREATE TABLE median_test (salary real); + INSERT INTO median_test VALUES (4500); + INSERT INTO median_test VALUES (NULL); + INSERT INTO median_test VALUES (2100); + INSERT INTO median_test VALUES (3600); + INSERT INTO median_test VALUES (4000); + SELECT into med median(salary) from median_test; + DROP TABLE median_test; + return med; + +END; +$$ LANGUAGE plpgsql; +CREATE FUNCTION checkMedianRealEven() RETURNS real AS $$ +DECLARE + med real; + +BEGIN + CREATE TABLE median_test (salary real); + INSERT INTO median_test VALUES (4500); + INSERT INTO median_test VALUES (1500); + INSERT INTO median_test VALUES (2100); + INSERT INTO median_test VALUES (3600); + INSERT INTO median_test VALUES (1000); + INSERT INTO median_test VALUES (4000); + select into med median(salary) from median_test; + DROP TABLE median_test; + return med; +END; +$$ LANGUAGE plpgsql; +CREATE FUNCTION checkMedianDoubleOdd() RETURNS double precision AS $$ +DECLARE + med double precision; +BEGIN + CREATE TABLE median_test (salary double precision); + INSERT INTO median_test VALUES (4500); + INSERT INTO median_test VALUES (1500); + INSERT INTO median_test VALUES (2100); + INSERT INTO median_test VALUES (3600); + INSERT INTO median_test VALUES (4000); + select into med median(salary) from median_test; + DROP TABLE median_test; + return med; +END; +$$ LANGUAGE plpgsql; +CREATE FUNCTION checkMedianDoubleEven() RETURNS double precision AS $$ +DECLARE + med double precision; + +BEGIN + CREATE TABLE median_test (salary double precision); + INSERT INTO median_test VALUES (4500); + INSERT INTO median_test VALUES (1500); + INSERT INTO median_test VALUES (2100); + INSERT INTO median_test VALUES (3600); + INSERT INTO median_test VALUES (4000); + INSERT INTO median_test VALUES (1000); + select into med median(salary) from median_test; + DROP TABLE median_test; + return med; +END; +$$ LANGUAGE plpgsql; +SELECT checkMedianRealOdd(); + checkmedianrealodd +-------------------- + 3800 +(1 row) + +SELECT checkMedianRealEven(); + checkmedianrealeven +--------------------- + 2850 +(1 row) + +SELECT checkMedianDoubleOdd(); + checkmediandoubleodd +---------------------- + 3600 +(1 row) + +SELECT checkMedianDoubleEven(); + checkmediandoubleeven +----------------------- + 2850 +(1 row) + +DROP FUNCTION checkMedianRealOdd(); +DROP FUNCTION checkMedianRealEven(); +DROP FUNCTION checkMedianDoubleOdd(); +DROP FUNCTION checkMedianDoubleEven(); diff --git a/src/postgres/src/test/regress/expected/yb_pg_orafce_dbms_alert_session_A.out b/src/postgres/src/test/regress/expected/yb_pg_orafce_dbms_alert_session_A.out new file mode 100644 index 000000000000..2b6a770cf679 --- /dev/null +++ b/src/postgres/src/test/regress/expected/yb_pg_orafce_dbms_alert_session_A.out @@ -0,0 +1,160 @@ +\set ECHO all +SELECT pg_sleep(3); + pg_sleep +---------- + +(1 row) + +/* + * DBMS_ALERT is used for one-way communication of one session to other. + * + * This session mainly sends signals for testing the alert functionality in + * session B and C. + * + * The following alerts are used to ensure that signals are sent at correct + * times to session B for testing. These signals are sent from session B + * indicating completion of an event. + * After the signal is received, the next required signal for testing is sent + * from this session. + */ +SELECT dbms_alert.register('b1'); + register +---------- + +(1 row) + +SELECT dbms_alert.register('b2'); + register +---------- + +(1 row) + +SELECT dbms_alert.register('b3'); + register +---------- + +(1 row) + +SELECT dbms_alert.register('b4'); + register +---------- + +(1 row) + +SELECT dbms_alert.register('b5'); + register +---------- + +(1 row) + +SELECT dbms_alert.signal('a1','Msg1 for a1'); +ERROR: CREATE CONSTRAINT TRIGGER not supported yet +LINE 1: CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON o... + ^ +HINT: See https://github.com/YugaByte/yugabyte-db/issues/1709. Click '+' on the description to raise its priority +QUERY: CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON ora_alerts INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE dbms_alert.defered_signal() +SELECT dbms_alert.signal('a2','Msg1 for a2'); +ERROR: CREATE CONSTRAINT TRIGGER not supported yet +LINE 1: CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON o... + ^ +HINT: See https://github.com/YugaByte/yugabyte-db/issues/1709. Click '+' on the description to raise its priority +QUERY: CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON ora_alerts INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE dbms_alert.defered_signal() +/* + * Test: defered_signal + * The signal is received only when the signalling transaction commits. + * To test this, an explict BEGIN-COMMIT block is used. + */ +SELECT dbms_alert.signal('tds','Begin defered_signal test'); +ERROR: CREATE CONSTRAINT TRIGGER not supported yet +LINE 1: CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON o... + ^ +HINT: See https://github.com/YugaByte/yugabyte-db/issues/1709. Click '+' on the description to raise its priority +QUERY: CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON ora_alerts INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE dbms_alert.defered_signal() +BEGIN; +SELECT dbms_alert.signal('tds','Testing defered_signal'); +ERROR: CREATE CONSTRAINT TRIGGER not supported yet +LINE 1: CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON o... + ^ +HINT: See https://github.com/YugaByte/yugabyte-db/issues/1709. Click '+' on the description to raise its priority +QUERY: CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON ora_alerts INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE dbms_alert.defered_signal() +/* The signal is received while transaction is running */ +SELECT dbms_alert.waitone('b1',20); +ERROR: current transaction is aborted, commands ignored until end of transaction block +COMMIT; +/* The signal is received after transaction completed. + * After this the tds signal is received in session B indicating that the + * signal is received only after commit. + */ +SELECT dbms_alert.waitone('b1',20); + waitone +--------- + (,1) +(1 row) + +SELECT dbms_alert.waitone('b2',20); + waitone +--------- + (,1) +(1 row) + +/* This signals a3 which is not registered in Session B */ +SELECT dbms_alert.signal('a3','Msg1 for a3'); +ERROR: CREATE CONSTRAINT TRIGGER not supported yet +LINE 1: CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON o... + ^ +HINT: See https://github.com/YugaByte/yugabyte-db/issues/1709. Click '+' on the description to raise its priority +QUERY: CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON ora_alerts INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE dbms_alert.defered_signal() +/* alert a4 is signalled soon after a3 */ +SELECT dbms_alert.signal('a4','Test- Register after signal'); +ERROR: CREATE CONSTRAINT TRIGGER not supported yet +LINE 1: CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON o... + ^ +HINT: See https://github.com/YugaByte/yugabyte-db/issues/1709. Click '+' on the description to raise its priority +QUERY: CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON ora_alerts INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE dbms_alert.defered_signal() +/* This signal indicates at remove() is called */ +SELECT dbms_alert.waitone('b3',20); + waitone +--------- + (,1) +(1 row) + +/* Send signal which is removed in session B */ +SELECT dbms_alert.signal('a1','Msg2 for a1'); +ERROR: CREATE CONSTRAINT TRIGGER not supported yet +LINE 1: CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON o... + ^ +HINT: See https://github.com/YugaByte/yugabyte-db/issues/1709. Click '+' on the description to raise its priority +QUERY: CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON ora_alerts INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE dbms_alert.defered_signal() +SELECT dbms_alert.waitone('b4',20); + waitone +--------- + (,1) +(1 row) + +/* Send signal which is registered in B and not removed */ +SELECT dbms_alert.signal('a4','Msg1 for a4'); +ERROR: CREATE CONSTRAINT TRIGGER not supported yet +LINE 1: CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON o... + ^ +HINT: See https://github.com/YugaByte/yugabyte-db/issues/1709. Click '+' on the description to raise its priority +QUERY: CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON ora_alerts INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE dbms_alert.defered_signal() +/* This signal inidcates that removeall() is called */ +SELECT dbms_alert.waitone('b5',20); + waitone +--------- + (,1) +(1 row) + +/* Send a signal to test if session B receives it after removeall() */ +SELECT dbms_alert.signal('a2','Msg2 for a2'); +ERROR: CREATE CONSTRAINT TRIGGER not supported yet +LINE 1: CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON o... + ^ +HINT: See https://github.com/YugaByte/yugabyte-db/issues/1709. Click '+' on the description to raise its priority +QUERY: CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON ora_alerts INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE dbms_alert.defered_signal() +/* cleanup */ +SELECT dbms_alert.removeall(); + removeall +----------- + +(1 row) diff --git a/src/postgres/src/test/regress/expected/yb_pg_orafce_dbms_alert_session_B.out b/src/postgres/src/test/regress/expected/yb_pg_orafce_dbms_alert_session_B.out new file mode 100644 index 000000000000..743e335a4eab --- /dev/null +++ b/src/postgres/src/test/regress/expected/yb_pg_orafce_dbms_alert_session_B.out @@ -0,0 +1,150 @@ +\set ECHO all +/* Register alerts */ +SELECT dbms_alert.register('a1'); + register +---------- + +(1 row) + +SELECT dbms_alert.register('a2'); + register +---------- + +(1 row) + +SELECT dbms_alert.register('tds'); + register +---------- + +(1 row) + +/* Test: multisession waitone */ +SELECT dbms_alert.waitone('a1',20); + waitone +--------- + (,1) +(1 row) + +/* Test: multisession waitany */ +SELECT dbms_alert.waitany(10); + waitany +--------- + (,,1) +(1 row) + +/* Test defered_signal */ +/* This indicated that the transaction has begun */ +SELECT dbms_alert.waitone('tds',10); + waitone +--------- + (,1) +(1 row) + +/* The signal will not be received because the transaction is running */ +SELECT dbms_alert.waitone('tds',2); + waitone +--------- + (,1) +(1 row) + +SELECT dbms_alert.signal('b1','Transaction still running'); +ERROR: CREATE CONSTRAINT TRIGGER not supported yet +LINE 1: CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON o... + ^ +HINT: See https://github.com/YugaByte/yugabyte-db/issues/1709. Click '+' on the description to raise its priority +QUERY: CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON ora_alerts INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE dbms_alert.defered_signal() +SELECT dbms_alert.signal('b1','Transaction committed'); +ERROR: CREATE CONSTRAINT TRIGGER not supported yet +LINE 1: CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON o... + ^ +HINT: See https://github.com/YugaByte/yugabyte-db/issues/1709. Click '+' on the description to raise its priority +QUERY: CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON ora_alerts INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE dbms_alert.defered_signal() +/* Since the transaction has commited, the signal will be received */ +SELECT dbms_alert.waitone('tds',10); + waitone +--------- + (,1) +(1 row) + +/* Signal session A to send msg1 for a3 */ +SELECT dbms_alert.signal('b2','to check unregistered alert wait'); +ERROR: CREATE CONSTRAINT TRIGGER not supported yet +LINE 1: CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON o... + ^ +HINT: See https://github.com/YugaByte/yugabyte-db/issues/1709. Click '+' on the description to raise its priority +QUERY: CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON ora_alerts INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE dbms_alert.defered_signal() +/* Test: wait for unregistered alert which is signaled*/ +SELECT dbms_alert.waitone('a3',2); + waitone +--------- + (,1) +(1 row) + +/* Test: Register after alert is signaled and wait */ +SELECT dbms_alert.register('a4'); + register +---------- + +(1 row) + +SELECT dbms_alert.waitone('a4',2); + waitone +--------- + (,1) +(1 row) + +/* Test: remove one */ +SELECT dbms_alert.remove('a1'); + remove +-------- + +(1 row) + +/* Signal session A to send msg2 for a1 */ +SELECT dbms_alert.signal('b3','remove(a1) called'); +ERROR: CREATE CONSTRAINT TRIGGER not supported yet +LINE 1: CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON o... + ^ +HINT: See https://github.com/YugaByte/yugabyte-db/issues/1709. Click '+' on the description to raise its priority +QUERY: CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON ora_alerts INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE dbms_alert.defered_signal() +/* Test: wait for removed alert */ +SELECT dbms_alert.waitone('a1',2); + waitone +--------- + (,1) +(1 row) + +/* Signal session A to send msg1 for a4 */ +SELECT dbms_alert.signal('b4','to check unremoved alert'); +ERROR: CREATE CONSTRAINT TRIGGER not supported yet +LINE 1: CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON o... + ^ +HINT: See https://github.com/YugaByte/yugabyte-db/issues/1709. Click '+' on the description to raise its priority +QUERY: CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON ora_alerts INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE dbms_alert.defered_signal() +/* Test: Check if unremoved alert is received */ +SELECT dbms_alert.waitone('a4',10); + waitone +--------- + (,1) +(1 row) + +/* Test removeall */ +SELECT dbms_alert.removeall(); + removeall +----------- + +(1 row) + +/* Signal session A to send msg2 for a2 */ +SELECT dbms_alert.signal('b5','removeall called'); +ERROR: CREATE CONSTRAINT TRIGGER not supported yet +LINE 1: CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON o... + ^ +HINT: See https://github.com/YugaByte/yugabyte-db/issues/1709. Click '+' on the description to raise its priority +QUERY: CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON ora_alerts INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE dbms_alert.defered_signal() +/* Test: Use waitany to see if any alert is received */ +SELECT dbms_alert.waitany(2); + waitany +--------- + (,,1) +(1 row) diff --git a/src/postgres/src/test/regress/expected/yb_pg_orafce_dbms_alert_session_C.out b/src/postgres/src/test/regress/expected/yb_pg_orafce_dbms_alert_session_C.out new file mode 100644 index 000000000000..54a42f06204d --- /dev/null +++ b/src/postgres/src/test/regress/expected/yb_pg_orafce_dbms_alert_session_C.out @@ -0,0 +1,34 @@ +\set ECHO all +/* Register alerts */ +SELECT dbms_alert.register('a1'); + register +---------- + +(1 row) + +SELECT dbms_alert.register('a2'); + register +---------- + +(1 row) + +/* Test: multisession waitone */ +SELECT dbms_alert.waitone('a1',20); + waitone +--------- + (,1) +(1 row) + +/* Test: multisession waitany */ +SELECT dbms_alert.waitany(10); + waitany +--------- + (,,1) +(1 row) + +/* cleanup */ +SELECT dbms_alert.removeall(); + removeall +----------- + +(1 row) diff --git a/src/postgres/src/test/regress/expected/yb_pg_orafce_dbms_output.out b/src/postgres/src/test/regress/expected/yb_pg_orafce_dbms_output.out new file mode 100644 index 000000000000..377c16468c48 --- /dev/null +++ b/src/postgres/src/test/regress/expected/yb_pg_orafce_dbms_output.out @@ -0,0 +1,1043 @@ +\set ECHO none +DROP FUNCTION dbms_output_test(); +ERROR: function dbms_output_test() does not exist +DROP TABLE dbms_output_test; +ERROR: table "dbms_output_test" does not exist +-- DBMS_OUTPUT.DISABLE [0] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER; +BEGIN + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); + dbms_output_test +------------------ + +(1 row) + +SELECT * FROM dbms_output_test order by buff asc; + buff | status +--------+-------- + | 1 +(1 row) + +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); +-- DBMS_OUTPUT.PUT_LINE [1] +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff1 VARCHAR(20) := 'orafce'; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('t'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE'); + PERFORM DBMS_OUTPUT.PUT_LINE (buff1); + PERFORM DBMS_OUTPUT.PUT ('ABC'); + PERFORM DBMS_OUTPUT.PUT_LINE (''); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +ORAFCE +orafce +ABC + dbms_output_test +------------------ + +(1 row) + +DROP FUNCTION dbms_output_test(); +-- DBMS_OUTPUT.PUT_LINE [2] +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('t'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORA +F +CE'); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +ORA +F +CE + dbms_output_test +------------------ + +(1 row) + +DROP FUNCTION dbms_output_test(); +-- DBMS_OUTPUT.PUT [1] +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff1 VARCHAR(20) := 'ora'; + buff2 VARCHAR(20) := 'f'; + buff3 VARCHAR(20) := 'ce'; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('t'); + PERFORM DBMS_OUTPUT.PUT ('ORA'); + PERFORM DBMS_OUTPUT.PUT ('F'); + PERFORM DBMS_OUTPUT.PUT ('CE'); + PERFORM DBMS_OUTPUT.PUT_LINE (''); + PERFORM DBMS_OUTPUT.PUT ('ABC'); + PERFORM DBMS_OUTPUT.PUT_LINE (''); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +ORAFCE +ABC + dbms_output_test +------------------ + +(1 row) + +DROP FUNCTION dbms_output_test(); +-- DBMS_OUTPUT.PUT [2] +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('t'); + PERFORM DBMS_OUTPUT.PUT ('ORA +F +CE'); + PERFORM DBMS_OUTPUT.PUT_LINE (''); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +ORA +F +CE + dbms_output_test +------------------ + +(1 row) + +DROP FUNCTION dbms_output_test(); +-- DBMS_OUTPUT.GET_LINE [1] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 2'); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); + dbms_output_test +------------------ + +(1 row) + +SELECT * FROM dbms_output_test order by buff asc; + buff | status +---------------+-------- + ORAFCE TEST 1 | 0 + ORAFCE TEST 2 | 0 +(2 rows) + +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); +-- DBMS_OUTPUT.GET_LINE [2] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 2'); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 3'); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); + dbms_output_test +------------------ + +(1 row) + +SELECT * FROM dbms_output_test order by buff asc; + buff | status +---------------+-------- + ORAFCE TEST 1 | 0 + ORAFCE TEST 3 | 0 + | 1 + | 1 +(4 rows) + +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); +-- DBMS_OUTPUT.GET_LINE [3] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 2'); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.PUT ('ORA'); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); + dbms_output_test +------------------ + +(1 row) + +SELECT * FROM dbms_output_test order by buff asc; + buff | status +---------------+-------- + ORA | 0 + ORAFCE TEST 1 | 0 + | 1 +(3 rows) + +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); +-- DBMS_OUTPUT.GET_LINE [4] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 2'); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.NEW_LINE(); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); + dbms_output_test +------------------ + +(1 row) + +SELECT * FROM dbms_output_test order by buff asc; + buff | status +---------------+-------- + | 0 + ORAFCE TEST 1 | 0 + | 1 +(3 rows) + +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); +-- DBMS_OUTPUT.GET_LINE [5] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1 +'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 2'); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); + dbms_output_test +------------------ + +(1 row) + +SELECT REPLACE(buff, ' +', '') FROM dbms_output_test order by buff asc; + replace +------------------- + ORAFCE TEST 1 + ORAFCE TEST 2 +(2 rows) + +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); +-- DBMS_OUTPUT.GET_LINE [6] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORA +F +CE'); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); + dbms_output_test +------------------ + +(1 row) + +SELECT regexp_replace(buff, E'\n', '', 'g') FROM dbms_output_test limit 1; + regexp_replace +---------------- + ORAFCE +(1 row) + +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); +-- DBMS_OUTPUT.GET_LINES [1] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + buff1 VARCHAR(20); + buff2 VARCHAR(20); + buff3 VARCHAR(20); + stts INTEGER := 10; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 2'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 3'); + SELECT INTO buff1,buff2,buff3,stts lines[1],lines[2],lines[3],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff1, stts); + INSERT INTO dbms_output_test VALUES (buff2, stts); + INSERT INTO dbms_output_test VALUES (buff3, stts); + SELECT INTO buff,stts lines[1],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); + dbms_output_test +------------------ + +(1 row) + +SELECT * FROM dbms_output_test order by buff asc; + buff | status +---------------+-------- + ORAFCE TEST 1 | 3 + ORAFCE TEST 2 | 3 + ORAFCE TEST 3 | 3 + | 0 +(4 rows) + +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); +-- DBMS_OUTPUT.GET_LINES [2] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + buff1 VARCHAR(20); + buff2 VARCHAR(20); + stts INTEGER := 2; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 2'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 3'); + SELECT INTO buff1,buff2,stts lines[1],lines[2],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff1, stts); + INSERT INTO dbms_output_test VALUES (buff2, stts); + SELECT INTO buff,stts lines[1],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); + dbms_output_test +------------------ + +(1 row) + +SELECT * FROM dbms_output_test order by buff asc; + buff | status +---------------+-------- + ORAFCE TEST 1 | 2 + ORAFCE TEST 2 | 2 + ORAFCE TEST 3 | 1 +(3 rows) + +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); +-- DBMS_OUTPUT.GET_LINES [3] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER := 1; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 2'); + SELECT INTO buff,stts lines[1],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 3'); + SELECT INTO buff,stts lines[1],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff, stts); + SELECT INTO buff,stts lines[1],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); + dbms_output_test +------------------ + +(1 row) + +SELECT * FROM dbms_output_test order by buff asc; + buff | status +---------------+-------- + ORAFCE TEST 1 | 1 + ORAFCE TEST 3 | 1 + | 0 +(3 rows) + +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); +-- DBMS_OUTPUT.GET_LINES [4] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER := 1; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 2'); + SELECT INTO buff,stts lines[1],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.PUT ('ORA'); + SELECT INTO buff,stts lines[1],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff, stts); + SELECT INTO buff,stts lines[1],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); + dbms_output_test +------------------ + +(1 row) + +SELECT * FROM dbms_output_test order by buff asc; + buff | status +---------------+-------- + ORA | 1 + ORAFCE TEST 1 | 1 + | 0 +(3 rows) + +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); +-- DBMS_OUTPUT.GET_LINES [5] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER := 1; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 2'); + SELECT INTO buff,stts lines[1],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.NEW_LINE(); + SELECT INTO buff,stts lines[1],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff, stts); + SELECT INTO buff,stts lines[1],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); + dbms_output_test +------------------ + +(1 row) + +SELECT * FROM dbms_output_test order by buff asc; + buff | status +---------------+-------- + | 1 + ORAFCE TEST 1 | 1 + | 0 +(3 rows) + +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); +-- DBMS_OUTPUT.GET_LINES [6] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER := 1; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORA +F +CE'); + SELECT INTO buff,stts lines[1],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); + dbms_output_test +------------------ + +(1 row) + +SELECT regexp_replace(buff, E'\n', '', 'g') FROM dbms_output_test limit 1; + regexp_replace +---------------- + ORAFCE +(1 row) + +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); +-- DBMS_OUTPUT.NEW_LINE [1] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff1 VARCHAR(20); + buff2 VARCHAR(20); + stts INTEGER := 10; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT ('ORA'); + PERFORM DBMS_OUTPUT.NEW_LINE(); + PERFORM DBMS_OUTPUT.PUT ('FCE'); + PERFORM DBMS_OUTPUT.NEW_LINE(); + SELECT INTO buff1,buff2,stts lines[1],lines[2],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff1, stts); + INSERT INTO dbms_output_test VALUES (buff2, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); + dbms_output_test +------------------ + +(1 row) + +SELECT * FROM dbms_output_test order by buff asc; + buff | status +------+-------- + FCE | 2 + ORA | 2 +(2 rows) + +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); +-- DBMS_OUTPUT.NEW_LINE [2] +CREATE TABLE dbms_output_test (buff VARCHAR(3000), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff1 VARCHAR(3000); + stts INTEGER := 10; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.ENABLE(2000); + FOR j IN 1..1999 LOOP + PERFORM DBMS_OUTPUT.PUT ('A'); + END LOOP; + PERFORM DBMS_OUTPUT.NEW_LINE(); + SELECT INTO buff1,stts lines[1],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff1, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); + dbms_output_test +------------------ + +(1 row) + +SELECT buff FROM dbms_output_test order by buff asc; + buff +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +(1 row) + +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); +-- DBMS_OUTPUT.DISABLE [1] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 2'); + PERFORM DBMS_OUTPUT.ENABLE(); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 3'); + PERFORM DBMS_OUTPUT.DISABLE(); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.ENABLE(); + + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.PUT ('ORAFCE TEST 4'); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.NEW_LINE(); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.PUT ('ORAFCE TEST 5'); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.NEW_LINE(); + PERFORM DBMS_OUTPUT.ENABLE(); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); + dbms_output_test +------------------ + +(1 row) + +SELECT * FROM dbms_output_test order by buff asc; + buff | status +--------+-------- + | 0 + | 1 + | 1 + | 1 + | 1 +(5 rows) + +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); +-- DBMS_OUTPUT.DISABLE [2] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER := 10; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + SELECT INTO buff,stts lines[1],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); + dbms_output_test +------------------ + +(1 row) + +SELECT * FROM dbms_output_test order by buff asc; + buff | status +--------+-------- + | 0 +(1 row) + +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); +-- DBMS_OUTPUT.ENABLE [1] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + status INTEGER; + num INTEGER := 2000; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('t'); + PERFORM DBMS_OUTPUT.ENABLE(2000); + PERFORM DBMS_OUTPUT.PUT ('ORAFCE TEST 1'); + PERFORM DBMS_OUTPUT.NEW_LINE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +ORAFCE TEST 1 + dbms_output_test +------------------ + +(1 row) + +SELECT * FROM dbms_output_test order by buff asc; + buff | status +------+-------- +(0 rows) + +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); +-- DBMS_OUTPUT.ENABLE [2] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.PUT ('ORAFCE TEST 2'); + PERFORM DBMS_OUTPUT.NEW_LINE(); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); + dbms_output_test +------------------ + +(1 row) + +SELECT * FROM dbms_output_test order by buff asc; + buff | status +---------------+-------- + ORAFCE TEST 1 | 0 + ORAFCE TEST 2 | 0 +(2 rows) + +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); +-- DBMS_OUTPUT.ENABLE [3] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER := 10; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + SELECT INTO buff,stts lines[1],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff, stts); + +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); + dbms_output_test +------------------ + +(1 row) + +SELECT * FROM dbms_output_test order by buff asc; + buff | status +---------------+-------- + ORAFCE TEST 1 | 1 +(1 row) + +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); +-- DBMS_OUTPUT.ENABLE [4] +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('t'); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + FOR j IN 1..2000 LOOP + PERFORM DBMS_OUTPUT.PUT ('A'); + END LOOP; + PERFORM DBMS_OUTPUT.NEW_LINE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + dbms_output_test +------------------ + +(1 row) + +DROP FUNCTION dbms_output_test(); +-- DBMS_OUTPUT.ENABLE [5] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(NULL); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); + dbms_output_test +------------------ + +(1 row) + +SELECT * FROM dbms_output_test order by buff asc; + buff | status +---------------+-------- + ORAFCE TEST 1 | 0 +(1 row) + +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); +-- DBMS_OUTPUT.ENABLE [6] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + PERFORM DBMS_OUTPUT.ENABLE(); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); + dbms_output_test +------------------ + +(1 row) + +SELECT * FROM dbms_output_test order by buff asc; + buff | status +---------------+-------- + ORAFCE TEST 1 | 0 +(1 row) + +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); +-- SERVEROUTPUT [1] +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); + dbms_output_test +------------------ + +(1 row) + +DROP FUNCTION dbms_output_test(); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +BEGIn + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('t'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 2'); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +ORAFCE TEST 2 + dbms_output_test +------------------ + +(1 row) + +DROP FUNCTION dbms_output_test(); +-- SERVEROUTPUT [2] +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT ('ORAFCE TEST 1'); + PERFORM DBMS_OUTPUT.NEW_LINE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); + dbms_output_test +------------------ + +(1 row) + +DROP FUNCTION dbms_output_test(); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('t'); + PERFORM DBMS_OUTPUT.PUT ('ORAFCE TEST 2'); + PERFORM DBMS_OUTPUT.NEW_LINE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +ORAFCE TEST 2 + dbms_output_test +------------------ + +(1 row) + +DROP FUNCTION dbms_output_test(); +-- SERVEROUTPUT [3] +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.DISABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); + dbms_output_test +------------------ + +(1 row) + +DROP FUNCTION dbms_output_test(); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('t'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +ORAFCE TEST 1 + dbms_output_test +------------------ + +(1 row) + +DROP FUNCTION dbms_output_test(); diff --git a/src/postgres/src/test/regress/expected/yb_pg_orafce_dbms_pipe_session_A.out b/src/postgres/src/test/regress/expected/yb_pg_orafce_dbms_pipe_session_A.out new file mode 100644 index 000000000000..5892a2ed8be4 --- /dev/null +++ b/src/postgres/src/test/regress/expected/yb_pg_orafce_dbms_pipe_session_A.out @@ -0,0 +1,113 @@ +\set ECHO none + pack_message +-------------- + +(1 row) + + send_message +-------------- + 0 +(1 row) + +SELECT createImplicitPipe(); + createimplicitpipe +-------------------- + +(1 row) + +-- Bulk send messages +SELECT bulkSend(); + bulksend +---------- + +(1 row) + +-- An explicit private pipe +SELECT notify('recv_private1_notifier'); + notify +-------- + +(1 row) + +SELECT createExplicitPipe('private_pipe_1',3); + createexplicitpipe +-------------------- + +(1 row) + +-- An explicit private pipe +SELECT notify('recv_private2_notifier'); + notify +-------- + +(1 row) + +SELECT createExplicitPipe('private_pipe_2',3); + createexplicitpipe +-------------------- + +(1 row) + +-- An explicit public pipe (uses two-argument create_pipe) +SELECT notify('recv_public1_notifier'); + notify +-------- + +(1 row) + +SELECT createExplicitPipe('public_pipe_3',2); + createexplicitpipe +-------------------- + +(1 row) + +-- An explicit public pipe (uses one-argument create_pipe) +SELECT notify('recv_public2_notifier'); + notify +-------- + +(1 row) + +SELECT createExplicitPipe('public_pipe_4',1); + createexplicitpipe +-------------------- + +(1 row) + +-- tests send_message(text) +SELECT checkSend1(); + checksend1 +------------ + +(1 row) + +-- tests send_message(text,integer) +SELECT checkSend2(); + checksend2 +------------ + +(1 row) + +SELECT notifyDropTemp(); + notifydroptemp +---------------- + +(1 row) + +-- tests unique_session_name() +SELECT checkUniqueSessionNameA(); + checkuniquesessionnamea +------------------------- + +(1 row) + +DROP FUNCTION createImplicitPipe(); +DROP FUNCTION createExplicitPipe(text,integer); +DROP FUNCTION createPipe(text,integer); +DROP FUNCTION checkSend1(); +DROP FUNCTION checkSend2(); +DROP FUNCTION checkUniqueSessionNameA(); +DROP FUNCTION bulkSend(); +DROP FUNCTION notifyDropTemp(); +DROP FUNCTION notify(text); +DROP FUNCTION send(text); diff --git a/src/postgres/src/test/regress/expected/yb_pg_orafce_dbms_pipe_session_B.out b/src/postgres/src/test/regress/expected/yb_pg_orafce_dbms_pipe_session_B.out new file mode 100644 index 000000000000..2d018d0d948a --- /dev/null +++ b/src/postgres/src/test/regress/expected/yb_pg_orafce_dbms_pipe_session_B.out @@ -0,0 +1,248 @@ +\set ECHO none + receive_message +----------------- + 0 +(1 row) + +-- Receives messages sent via an implicit pipe +SELECT receiveFrom('named_pipe'); +NOTICE: RECEIVE 11: Message From Session A +NOTICE: RECEIVE 12: 01-01-2013 +NOTICE: RECEIVE 13: Tue Jan 01 09:00:00 2013 PST +NOTICE: RECEIVE 13: Tue Jan 01 09:00:00 2013 PST +NOTICE: RECEIVE 9: 12345.6789 +NOTICE: RECEIVE 9: 12345 +NOTICE: RECEIVE 9: 99999999999 +NOTICE: RECEIVE 23: \201 +NOTICE: RECEIVE 24: (2,rob) + receivefrom +------------- + +(1 row) + +-- Bulk receive messages +SELECT bulkReceive(); +NOTICE: RECEIVE 11: Message From Session A +NOTICE: RECEIVE 12: 01-01-2013 +NOTICE: RECEIVE 13: Tue Jan 01 09:00:00 2013 PST +NOTICE: RECEIVE 13: Tue Jan 01 09:00:00 2013 PST +NOTICE: RECEIVE 9: 12345.6789 +NOTICE: RECEIVE 9: 12345 +NOTICE: RECEIVE 9: 99999999999 +NOTICE: RECEIVE 23: \201 +NOTICE: RECEIVE 24: (2,rob) + bulkreceive +------------- + +(1 row) + +-- Receives messages sent via an explicit private pipe under the same user +-- 'pipe_test_owner' +SELECT dbms_pipe.receive_message('recv_private1_notifier'); + receive_message +----------------- + 0 +(1 row) + +SELECT receiveFrom('private_pipe_1'); +NOTICE: RECEIVE 11: Message From Session A +NOTICE: RECEIVE 12: 01-01-2013 +NOTICE: RECEIVE 13: Tue Jan 01 09:00:00 2013 PST +NOTICE: RECEIVE 13: Tue Jan 01 09:00:00 2013 PST +NOTICE: RECEIVE 9: 12345.6789 +NOTICE: RECEIVE 9: 12345 +NOTICE: RECEIVE 9: 99999999999 +NOTICE: RECEIVE 23: \201 +NOTICE: RECEIVE 24: (2,rob) + receivefrom +------------- + +(1 row) + +-- Switch user to 'pipe_test_other' +DROP USER IF EXISTS pipe_test_other; +NOTICE: role "pipe_test_other" does not exist, skipping +CREATE USER pipe_test_other; +SET SESSION AUTHORIZATION pipe_test_other; +-- Try to receive messages sent via an explicit private pipe under the user +-- 'pipe_test_other' who is not the owner of pipe. +-- insufficient privileges in case of 'private_pipe_2'. +SELECT dbms_pipe.receive_message('recv_private2_notifier'); + receive_message +----------------- + 0 +(1 row) + +SELECT receiveFrom('private_pipe_2'); +ERROR: insufficient privilege +-- These are explicit private pipes created using create_pipe(text,integer) +-- and create_pipe(text) +SELECT dbms_pipe.receive_message('recv_public1_notifier'); + receive_message +----------------- + 0 +(1 row) + +SELECT receiveFrom('public_pipe_3'); +NOTICE: RECEIVE 11: Message From Session A +NOTICE: RECEIVE 12: 01-01-2013 +NOTICE: RECEIVE 13: Tue Jan 01 09:00:00 2013 PST +NOTICE: RECEIVE 13: Tue Jan 01 09:00:00 2013 PST +NOTICE: RECEIVE 9: 12345.6789 +NOTICE: RECEIVE 9: 12345 +NOTICE: RECEIVE 9: 99999999999 +NOTICE: RECEIVE 23: \201 +NOTICE: RECEIVE 24: (2,rob) + receivefrom +------------- + +(1 row) + +SELECT dbms_pipe.receive_message('recv_public2_notifier'); + receive_message +----------------- + 0 +(1 row) + +SELECT receiveFrom('public_pipe_4'); +NOTICE: RECEIVE 11: Message From Session A +NOTICE: RECEIVE 12: 01-01-2013 +NOTICE: RECEIVE 13: Tue Jan 01 09:00:00 2013 PST +NOTICE: RECEIVE 13: Tue Jan 01 09:00:00 2013 PST +NOTICE: RECEIVE 9: 12345.6789 +NOTICE: RECEIVE 9: 12345 +NOTICE: RECEIVE 9: 99999999999 +NOTICE: RECEIVE 23: \201 +NOTICE: RECEIVE 24: (2,rob) + receivefrom +------------- + +(1 row) + +-- Switch back to user 'pipe_test_owner' +SET SESSION AUTHORIZATION pipe_test_owner; +DROP USER pipe_test_other; +-- Tests receive_message(text) +SELECT checkReceive1('pipe_name_1'); +NOTICE: RECEIVE checking one-argument send_message() + checkreceive1 +--------------- + +(1 row) + +SELECT checkReceive1('pipe_name_2'); +NOTICE: RECEIVE checking two-argument send_message() + checkreceive1 +--------------- + +(1 row) + +-- Tests dbms_pipe.db_pipes view +SELECT name, items, "limit", private, owner +FROM dbms_pipe.db_pipes +WHERE name LIKE 'private%' +ORDER BY name; + name | items | limit | private | owner +----------------+-------+-------+---------+----------------- + private_pipe_1 | 0 | 10 | t | pipe_test_owner + private_pipe_2 | 9 | 10 | t | pipe_test_owner +(2 rows) + +-- Tests dbms_pipe.__list_pipes(); attribute size is not included +-- since it can be different across runs. +SELECT name, items, "limit", private, owner +FROM dbms_pipe.__list_pipes() AS (name varchar, items int4, siz int4, "limit" int4, private bool, owner varchar) +WHERE name <> 'pipe_name_4' +ORDER BY 1; + name | items | limit | private | owner +----------------+-------+-------+---------+----------------- + pipe_name_3 | 1 | | f | + private_pipe_1 | 0 | 10 | t | pipe_test_owner + private_pipe_2 | 9 | 10 | t | pipe_test_owner + public_pipe_3 | 0 | 10 | f | + public_pipe_4 | 0 | 10 | f | +(5 rows) + +-- Tests remove_pipe(text) +SELECT dbms_pipe.remove_pipe('private_pipe_1'); + remove_pipe +------------- + +(1 row) + +SELECT dbms_pipe.remove_pipe('private_pipe_2'); + remove_pipe +------------- + +(1 row) + +SELECT dbms_pipe.remove_pipe('public_pipe_3'); + remove_pipe +------------- + +(1 row) + +SELECT dbms_pipe.remove_pipe('public_pipe_4'); + remove_pipe +------------- + +(1 row) + +SELECT dbms_pipe.purge('pipe_name_1'); + purge +------- + +(1 row) + +SELECT dbms_pipe.purge('pipe_name_2'); + purge +------- + +(1 row) + +-- Receives drop table notification from session A via 'pipe_name_3' +SELECT dropTempTable(); + droptemptable +--------------- + +(1 row) + +SELECT dbms_pipe.purge('pipe_name_3'); + purge +------- + +(1 row) + +-- tests unique_session_name() (uses 'pipe_name_4') +SELECT checkUniqueSessionNameB(); + checkuniquesessionnameb +------------------------- + f +(1 row) + +SELECT dbms_pipe.purge('pipe_name_4'); + purge +------- + +(1 row) + +DROP FUNCTION receiveFrom(text); +DROP FUNCTION checkReceive1(text); +DROP FUNCTION checkUniqueSessionNameB(); +DROP FUNCTION bulkReceive(); +DROP FUNCTION dropTempTable(); +-- Perform a recieve on removed pipe resulting on timeout +SELECT dbms_pipe.receive_message('public_pipe_4',2); + receive_message +----------------- + 1 +(1 row) + +SELECT dbms_pipe.purge('public_pipe_4'); + purge +------- + +(1 row) + +SET SESSION AUTHORIZATION DEFAULT; +DROP USER pipe_test_owner; diff --git a/src/postgres/src/test/regress/expected/yb_pg_orafce_dbms_random.out b/src/postgres/src/test/regress/expected/yb_pg_orafce_dbms_random.out new file mode 100644 index 000000000000..e5f4d0cd2762 --- /dev/null +++ b/src/postgres/src/test/regress/expected/yb_pg_orafce_dbms_random.out @@ -0,0 +1,84 @@ +-- Tests for package DBMS_RANDOM +SELECT dbms_random.initialize(8); + initialize +------------ + +(1 row) + +SELECT dbms_random.normal()::numeric(10, 8) != dbms_random.normal()::numeric(10, 8); + ?column? +---------- + t +(1 row) + +SELECT dbms_random.seed(8); + seed +------ + +(1 row) + +SELECT dbms_random.random() != dbms_random.random(); + ?column? +---------- + t +(1 row) + +SELECT dbms_random.seed('test'); + seed +------ + +(1 row) + +SELECT dbms_random.string('U',5) != dbms_random.string('U',5); + ?column? +---------- + t +(1 row) + +SELECT dbms_random.string('U',5) != dbms_random.string('P',2); + ?column? +---------- + t +(1 row) + +SELECT dbms_random.string('U',5) != dbms_random.string('x',4); + ?column? +---------- + t +(1 row) + +SELECT dbms_random.string('U',5) != dbms_random.string('a',2); + ?column? +---------- + t +(1 row) + +SELECT dbms_random.string('U',5) != dbms_random.string('l',3); + ?column? +---------- + t +(1 row) + +SELECT dbms_random.seed(5); + seed +------ + +(1 row) + +SELECT dbms_random.value()::numeric(10, 8) != dbms_random.value()::numeric(10, 8); + ?column? +---------- + t +(1 row) + +SELECT dbms_random.value(10,15)::numeric(10, 8) != dbms_random.value(10,15)::numeric(10, 8);; + ?column? +---------- + t +(1 row) + +SELECT dbms_random.terminate(); + terminate +----------- + +(1 row) diff --git a/src/postgres/src/test/regress/expected/yb_pg_orafce_dbms_utility.out b/src/postgres/src/test/regress/expected/yb_pg_orafce_dbms_utility.out new file mode 100644 index 000000000000..5971ea063228 --- /dev/null +++ b/src/postgres/src/test/regress/expected/yb_pg_orafce_dbms_utility.out @@ -0,0 +1,17 @@ +\set ECHO none +checkhexcallstack +----- PL/pgSQL Call Stack ----- + object line object + handle number name + 0 function anonymous object + 0 function checkhexcallstack +(1 row) +checkintcallstack + 0 function anonymous object + 0 function checkintcallstack +(1 row) +checkintunpaddedcallstack +0,,anonymous object +0,,checkintunpaddedcallstack +(1 row) +WARNING: there is no transaction in progress diff --git a/src/postgres/src/test/regress/expected/yb_pg_orafce_files.out b/src/postgres/src/test/regress/expected/yb_pg_orafce_files.out new file mode 100644 index 000000000000..c159d6e5d67d --- /dev/null +++ b/src/postgres/src/test/regress/expected/yb_pg_orafce_files.out @@ -0,0 +1,256 @@ +SET client_min_messages = NOTICE; +\set VERBOSITY terse +\set ECHO all +CREATE OR REPLACE FUNCTION gen_file(dir text) RETURNS void AS $$ +DECLARE + f utl_file.file_type; +BEGIN + f := utl_file.fopen(dir, 'regress_orafce.txt', 'w'); + PERFORM utl_file.put_line(f, 'ABC'); + PERFORM utl_file.put_line(f, '123'::numeric); + PERFORM utl_file.put_line(f, '-----'); + PERFORM utl_file.new_line(f); + PERFORM utl_file.put_line(f, '-----'); + PERFORM utl_file.new_line(f, 0); + PERFORM utl_file.put_line(f, '-----'); + PERFORM utl_file.new_line(f, 2); + PERFORM utl_file.put_line(f, '-----'); + PERFORM utl_file.put(f, 'A'); + PERFORM utl_file.put(f, 'B'); + PERFORM utl_file.new_line(f); + PERFORM utl_file.putf(f, '[1=%s, 2=%s, 3=%s, 4=%s, 5=%s]', '1', '2', '3', '4', '5'); + PERFORM utl_file.new_line(f); + PERFORM utl_file.put_line(f, '1234567890'); + f := utl_file.fclose(f); +END; +$$ LANGUAGE plpgsql; +/* Test functions utl_file.fflush(utl_file.file_type) and + * utl_file.get_nextline(utl_file.file_type) + * This function tests the positive test case of fflush by reading from the + * file after flushing the contents to the file. + */ +CREATE OR REPLACE FUNCTION checkFlushFile(dir text) RETURNS void AS $$ +DECLARE + f utl_file.file_type; + f1 utl_file.file_type; + ret_val text; + i integer; +BEGIN + f := utl_file.fopen(dir, 'regressflush_orafce.txt', 'a'); + PERFORM utl_file.put_line(f, 'ABC'); + PERFORM utl_file.new_line(f); + PERFORM utl_file.put_line(f, '123'::numeric); + PERFORM utl_file.new_line(f); + PERFORM utl_file.putf(f, '[1=%s, 2=%s, 3=%s, 4=%s, 5=%s]', '1', '2', '3', '4', '5'); + PERFORM utl_file.fflush(f); + f1 := utl_file.fopen(dir, 'regressflush_orafce.txt', 'r'); + ret_val=utl_file.get_nextline(f1); + i:=1; + WHILE ret_val IS NOT NULL LOOP + RAISE NOTICE '[%] >>%<<', i,ret_val; + ret_val := utl_file.get_nextline(f1); + i:=i+1; + END LOOP; + RAISE NOTICE '>>%<<', ret_val; + f1 := utl_file.fclose(f1); + f := utl_file.fclose(f); +END; +$$ LANGUAGE plpgsql; +CREATE OR REPLACE FUNCTION read_file(dir text) RETURNS void AS $$ +DECLARE + f utl_file.file_type; +BEGIN + f := utl_file.fopen(dir, 'regress_orafce.txt', 'r'); + FOR i IN 1..11 LOOP + RAISE NOTICE '[%] >>%<<', i, utl_file.get_line(f); + END LOOP; + RAISE NOTICE '>>%<<', utl_file.get_line(f, 4); + RAISE NOTICE '>>%<<', utl_file.get_line(f, 4); + RAISE NOTICE '>>%<<', utl_file.get_line(f); + RAISE NOTICE '>>%<<', utl_file.get_line(f); + EXCEPTION + -- WHEN no_data_found THEN, 8.1 plpgsql doesn't know no_data_found + WHEN others THEN + RAISE NOTICE 'finish % ', sqlerrm; + RAISE NOTICE 'is_open = %', utl_file.is_open(f); + PERFORM utl_file.fclose_all(); + RAISE NOTICE 'is_open = %', utl_file.is_open(f); + END; +$$ LANGUAGE plpgsql; +SELECT EXISTS(SELECT * FROM pg_catalog.pg_class where relname='utl_file_dir') AS exists; + exists +-------- + t +(1 row) + +SELECT EXISTS(SELECT * FROM pg_catalog.pg_type where typname='file_type') AS exists; + exists +-------- + t +(1 row) + +-- Trying to access a file in path not registered +SELECT utl_file.fopen(utl_file.tmpdir(),'sample.txt','r'); +ERROR: UTL_FILE_INVALID_PATH +-- Trying to access file in a non-existent directory +INSERT INTO utl_file.utl_file_dir(dir) VALUES('test_tmp_dir'); +SELECT utl_file.fopen('test_tmp_dir','file.txt.','w'); +ERROR: UTL_FILE_INVALID_PATH +DELETE FROM utl_file.utl_file_dir WHERE dir LIKE 'test_tmp_dir'; +-- Add tmpdir() to utl_file_dir table +INSERT INTO utl_file.utl_file_dir(dir) VALUES(utl_file.tmpdir()); +SELECT count(*) from utl_file.utl_file_dir where dir <> ''; + count +------- + 1 +(1 row) + +-- Trying to access non-existent file +SELECT utl_file.fopen(utl_file.tmpdir(),'non_existent_file.txt','r'); +ERROR: UTL_FILE_INVALID_PATH +--Other test cases +--run this under unprivileged user +CREATE ROLE test_role_files LOGIN; +SET ROLE TO test_role_files; +-- should to fail, unpriviliged user cannot to change utl_file_dir +INSERT INTO utl_file.utl_file_dir(dir) VALUES('test_tmp_dir'); +ERROR: permission denied for table utl_file_dir +SELECT gen_file(utl_file.tmpdir()); + gen_file +---------- + +(1 row) + +SELECT fexists FROM utl_file.fgetattr(utl_file.tmpdir(), 'regress_orafce.txt'); + fexists +--------- + t +(1 row) + +SELECT utl_file.fcopy(utl_file.tmpdir(), 'regress_orafce.txt', utl_file.tmpdir(), 'regress_orafce2.txt'); + fcopy +------- + +(1 row) + +SELECT fexists FROM utl_file.fgetattr(utl_file.tmpdir(), 'regress_orafce2.txt'); + fexists +--------- + t +(1 row) + +SELECT utl_file.frename(utl_file.tmpdir(), 'regress_orafce2.txt', utl_file.tmpdir(), 'regress_orafce.txt', true); + frename +--------- + +(1 row) + +SELECT fexists FROM utl_file.fgetattr(utl_file.tmpdir(), 'regress_orafce.txt'); + fexists +--------- + t +(1 row) + +SELECT fexists FROM utl_file.fgetattr(utl_file.tmpdir(), 'regress_orafce2.txt'); + fexists +--------- + f +(1 row) + +SELECT read_file(utl_file.tmpdir()); +NOTICE: [1] >>ABC<< +NOTICE: [2] >>123<< +NOTICE: [3] >>-----<< +NOTICE: [4] >><< +NOTICE: [5] >>-----<< +NOTICE: [6] >>-----<< +NOTICE: [7] >><< +NOTICE: [8] >><< +NOTICE: [9] >>-----<< +NOTICE: [10] >>AB<< +NOTICE: [11] >>[1=1, 2=2, 3=3, 4=4, 5=5]<< +NOTICE: >>1234<< +NOTICE: >>5678<< +NOTICE: >>90<< +NOTICE: finish no data found +NOTICE: is_open = t +NOTICE: is_open = f + read_file +----------- + +(1 row) + +SELECT utl_file.fremove(utl_file.tmpdir(), 'regress_orafce.txt'); + fremove +--------- + +(1 row) + +SELECT fexists FROM utl_file.fgetattr(utl_file.tmpdir(), 'regress_orafce.txt'); + fexists +--------- + f +(1 row) + +SELECT checkFlushFile(utl_file.tmpdir()); +NOTICE: [1] >>ABC<< +NOTICE: [2] >><< +NOTICE: [3] >>123<< +NOTICE: [4] >><< +NOTICE: [5] >>[1=1, 2=2, 3=3, 4=4, 5=5]<< +NOTICE: >><< + checkflushfile +---------------- + +(1 row) + +SELECT utl_file.fremove(utl_file.tmpdir(), 'regressflush_orafce.txt'); + fremove +--------- + +(1 row) + +SET ROLE TO DEFAULT; +DROP ROLE test_role_files; +DROP FUNCTION checkFlushFile(text); +DELETE FROM utl_file.utl_file_dir; +-- try to use named directory +INSERT INTO utl_file.utl_file_dir(dir, dirname) VALUES(utl_file.tmpdir(), 'TMPDIR'); +SELECT gen_file('TMPDIR'); + gen_file +---------- + +(1 row) + +SELECT read_file('TMPDIR'); +NOTICE: [1] >>ABC<< +NOTICE: [2] >>123<< +NOTICE: [3] >>-----<< +NOTICE: [4] >><< +NOTICE: [5] >>-----<< +NOTICE: [6] >>-----<< +NOTICE: [7] >><< +NOTICE: [8] >><< +NOTICE: [9] >>-----<< +NOTICE: [10] >>AB<< +NOTICE: [11] >>[1=1, 2=2, 3=3, 4=4, 5=5]<< +NOTICE: >>1234<< +NOTICE: >>5678<< +NOTICE: >>90<< +NOTICE: finish no data found +NOTICE: is_open = t +NOTICE: is_open = f + read_file +----------- + +(1 row) + +SELECT utl_file.fremove('TMPDIR', 'regress_orafce.txt'); + fremove +--------- + +(1 row) + +DROP FUNCTION gen_file(text); +DROP FUNCTION read_file(text); +DELETE FROM utl_file.utl_file_dir; diff --git a/src/postgres/src/test/regress/expected/yb_pg_orafce_init.out b/src/postgres/src/test/regress/expected/yb_pg_orafce_init.out new file mode 100644 index 000000000000..d202b2786b00 --- /dev/null +++ b/src/postgres/src/test/regress/expected/yb_pg_orafce_init.out @@ -0,0 +1,2 @@ +\set ECHO none +WARNING: there is no transaction in progress diff --git a/src/postgres/src/test/regress/expected/yb_pg_orafce_nlssort.out b/src/postgres/src/test/regress/expected/yb_pg_orafce_nlssort.out new file mode 100644 index 000000000000..db9390eda9e7 --- /dev/null +++ b/src/postgres/src/test/regress/expected/yb_pg_orafce_nlssort.out @@ -0,0 +1,40 @@ +-- Tests for nlssort +-- YB: we do not support SQL_ASCII encoding +\set ECHO none + name +-------- + Purple + brown + red + yellow +(4 rows) + + name +-------- + Purple + brown + red + yellow +(4 rows) + + set_nls_sort +-------------- + +(1 row) + +ERROR: failed to set the requested LC_COLLATE value [invalid] +CONTEXT: SQL function "nlssort" statement 1 + set_nls_sort +-------------- + +(1 row) + + name +-------- + Purple + brown + red + yellow +(4 rows) + +WARNING: there is no transaction in progress diff --git a/src/postgres/src/test/regress/expected/yb_pg_orafce_nvarchar2.out b/src/postgres/src/test/regress/expected/yb_pg_orafce_nvarchar2.out new file mode 100644 index 000000000000..320e7a9d0122 --- /dev/null +++ b/src/postgres/src/test/regress/expected/yb_pg_orafce_nvarchar2.out @@ -0,0 +1,66 @@ +\set VERBOSITY terse +SET client_encoding = utf8; +-- +-- test type modifier related rules +-- +-- ERROR (typmod >= 1) +CREATE TABLE bar (a NVARCHAR2(0)); +ERROR: length for type varchar must be at least 1 at character 21 +-- ERROR (number of typmods = 1) +CREATE TABLE bar (a NVARCHAR2(10, 1)); +ERROR: invalid type modifier at character 21 +-- OK +CREATE TABLE bar (a VARCHAR(5000)); +CREATE INDEX ON bar(a); +-- cleanup +DROP TABLE bar; +-- OK +CREATE TABLE bar (a NVARCHAR2(5)); +-- +-- test that no value longer than maxlen is allowed +-- +-- ERROR (length > 5) +INSERT INTO bar VALUES ('abcdef'); +ERROR: input value too long for type nvarchar2(5) +-- ERROR (length > 5); +-- NVARCHAR2 does not truncate blank spaces on implicit coercion +INSERT INTO bar VALUES ('abcde '); +ERROR: input value too long for type nvarchar2(5) +-- OK +INSERT INTO bar VALUES ('abcde'); +-- OK +INSERT INTO bar VALUES ('abcdef'::NVARCHAR2(5)); +-- OK +INSERT INTO bar VALUES ('abcde '::NVARCHAR2(5)); +--OK +INSERT INTO bar VALUES ('abc'::NVARCHAR2(5)); +-- +-- test whitespace semantics on comparison +-- +-- equal +SELECT 'abcde '::NVARCHAR2(10) = 'abcde '::NVARCHAR2(10); + ?column? +---------- + t +(1 row) + +-- not equal +SELECT 'abcde '::NVARCHAR2(10) = 'abcde '::NVARCHAR2(10); + ?column? +---------- + f +(1 row) + +-- null safe concat (disabled by default) +SELECT NULL || 'hello'::varchar2 || NULL; + ?column? +---------- + +(1 row) + +SET orafce.varchar2_null_safe_concat TO true; +SELECT NULL || 'hello'::varchar2 || NULL; + ?column? +---------- + hello +(1 row) diff --git a/src/postgres/src/test/regress/expected/yb_pg_orafce_varchar2.out b/src/postgres/src/test/regress/expected/yb_pg_orafce_varchar2.out new file mode 100644 index 000000000000..85e74a0b50c2 --- /dev/null +++ b/src/postgres/src/test/regress/expected/yb_pg_orafce_varchar2.out @@ -0,0 +1,149 @@ +\set VERBOSITY terse +SET client_encoding = utf8; +-- +-- test type modifier related rules +-- +-- ERROR (typmod >= 1) +CREATE TABLE foo (a VARCHAR2(0)); +ERROR: length for type varchar must be at least 1 at character 21 +-- ERROR (number of typmods = 1) +CREATE TABLE foo (a VARCHAR2(10, 1)); +ERROR: invalid type modifier at character 21 +-- OK +CREATE TABLE foo (a VARCHAR(5000)); +-- cleanup +DROP TABLE foo; +-- OK +CREATE TABLE foo (a VARCHAR2(5)); +CREATE INDEX ON foo(a); +ERROR: INDEX on column of type 'user_defined_type' not yet supported +-- +-- test that no value longer than maxlen is allowed +-- +-- ERROR (length > 5) +INSERT INTO foo VALUES ('abcdef'); +ERROR: input value length is 6; too long for type varchar2(5) +-- ERROR (length > 5); +-- VARCHAR2 does not truncate blank spaces on implicit coercion +INSERT INTO foo VALUES ('abcde '); +ERROR: input value length is 7; too long for type varchar2(5) +-- OK +INSERT INTO foo VALUES ('abcde'); +-- OK +INSERT INTO foo VALUES ('abcdef'::VARCHAR2(5)); +-- OK +INSERT INTO foo VALUES ('abcde '::VARCHAR2(5)); +--OK +INSERT INTO foo VALUES ('abc'::VARCHAR2(5)); +-- +-- test whitespace semantics on comparison +-- +-- equal +SELECT 'abcde '::VARCHAR2(10) = 'abcde '::VARCHAR2(10); + ?column? +---------- + t +(1 row) + +-- not equal +SELECT 'abcde '::VARCHAR2(10) = 'abcde '::VARCHAR2(10); + ?column? +---------- + f +(1 row) + +-- +-- test string functions created for varchar2 +-- +-- substrb(varchar2, int, int) +SELECT substrb('ABCありがとう'::VARCHAR2, 7, 6); + substrb +--------- + りが +(1 row) + +-- returns 'f' (emtpy string is not NULL) +SELECT substrb('ABCありがとう'::VARCHAR2, 7, 0) IS NULL; + ?column? +---------- + f +(1 row) + +-- If the starting position is zero or less, then return from the start +-- of the string adjusting the length to be consistent with the "negative start" +-- per SQL. +SELECT substrb('ABCありがとう'::VARCHAR2, 0, 4); + substrb +--------- + ABC +(1 row) + +-- substrb(varchar2, int) +SELECT substrb('ABCありがとう', 5); + substrb +---------- + りがとう +(1 row) + +-- strposb(varchar2, varchar2) +SELECT strposb('ABCありがとう', 'りが'); + strposb +--------- + 7 +(1 row) + +-- returns 1 (start of the source string) +SELECT strposb('ABCありがとう', ''); + strposb +--------- + 1 +(1 row) + +-- returns 0 +SELECT strposb('ABCありがとう', 'XX'); + strposb +--------- + 0 +(1 row) + +-- returns 't' +SELECT strposb('ABCありがとう', NULL) IS NULL; + ?column? +---------- + t +(1 row) + +-- lengthb(varchar2) +SELECT lengthb('ABCありがとう'); + lengthb +--------- + 18 +(1 row) + +-- returns 0 +SELECT lengthb(''); + lengthb +--------- + 0 +(1 row) + +-- returs 't' +SELECT lengthb(NULL) IS NULL; + ?column? +---------- + t +(1 row) + +-- null safe concat (disabled by default) +SELECT NULL || 'hello'::varchar2 || NULL; + ?column? +---------- + +(1 row) + +SET orafce.varchar2_null_safe_concat TO true; +SELECT NULL || 'hello'::varchar2 || NULL; + ?column? +---------- + hello +(1 row) diff --git a/src/postgres/src/test/regress/sql/yb_pg_orafce.sql b/src/postgres/src/test/regress/sql/yb_pg_orafce.sql new file mode 100644 index 000000000000..80b3e4cb56a9 --- /dev/null +++ b/src/postgres/src/test/regress/sql/yb_pg_orafce.sql @@ -0,0 +1,1032 @@ +\set ECHO none +SET client_min_messages = warning; +SET DATESTYLE TO ISO; +SET client_encoding = utf8; +\set ECHO all + +-- +-- test built-in date type oracle compatibility functions +-- + +SELECT add_months ('2003-08-01', 3); +SELECT add_months ('2003-08-01', -3); +SELECT add_months ('2003-08-21', -3); +SELECT add_months ('2003-01-31', 1); +SELECT add_months ('2008-02-28', 1); +SELECT add_months ('2008-02-29', 1); +SELECT add_months ('2008-01-31', 12); +SELECT add_months ('2008-01-31', -12); +SELECT add_months ('2008-01-31', 95903); +SELECT add_months ('2008-01-31', -80640); +SELECT add_months ('03-21-2008',3); +SELECT add_months ('21-MAR-2008',3); +SELECT add_months ('21-MAR-08',3); +SELECT add_months ('2008-MAR-21',3); +SELECT add_months ('March 21,2008',3); +SELECT add_months('03/21/2008',3); +SELECT add_months('20080321',3); +SELECT add_months('080321',3); + +SET search_path TO oracle,"$user", public, pg_catalog; +SELECT add_months ('2003-08-01 10:12:21', 3); +SELECT add_months ('2003-08-01 10:21:21', -3); +SELECT add_months ('2003-08-21 12:21:21', -3); +SELECT add_months ('2003-01-31 01:12:45', 1); +SELECT add_months ('2008-02-28 02:12:12', 1); +SELECT add_months ('2008-02-29 12:12:12', 1); +SELECT add_months ('2008-01-31 11:11:21', 12); +SELECT add_months ('2008-01-31 11:21:21', -12); +SELECT add_months ('2008-01-31 12:12:12', 95903); +SELECT add_months ('2008-01-31 11:32:12', -80640); +SELECT add_months ('03-21-2008 08:12:22',3); +SELECT add_months ('21-MAR-2008 06:02:12',3); +SELECT add_months ('21-MAR-08 12:11:22',3); +SELECT add_months ('2008-MAR-21 11:32:43',3); +SELECT add_months ('March 21,2008 12:32:12',3); +SELECT add_months('03/21/2008 12:32:12',3); +SELECT add_months('20080321 123244',3); +SELECT add_months('080321 121212',3); +SET search_path TO default; + +SELECT last_day(to_date('2003/03/15', 'yyyy/mm/dd')); +SELECT last_day(to_date('2003/02/03', 'yyyy/mm/dd')); +SELECT last_day(to_date('2004/02/03', 'yyyy/mm/dd')); +SELECT last_day('1900-02-01'); +SELECT last_day('2000-02-01'); +SELECT last_day('2007-02-01'); +SELECT last_day('2008-02-01'); + +SET search_path TO oracle,"$user", public, pg_catalog; +SELECT last_day(to_date('2003/03/15 11:12:21', 'yyyy/mm/dd hh:mi:ss')); +SELECT last_day(to_date('2003/02/03 10:21:32', 'yyyy/mm/dd hh:mi:ss')); +SELECT last_day(to_date('2004/02/03 11:32:12', 'yyyy/mm/dd hh:mi:ss')); +SELECT last_day('1900-02-01 12:12:11'); +SELECT last_day('2000-02-01 121143'); +SELECT last_day('2007-02-01 12:21:33'); +SELECT last_day('2008-02-01 121212'); +SET search_path TO default; + +SELECT next_day ('2003-08-01', 'TUESDAY'); +SELECT next_day ('2003-08-06', 'WEDNESDAY'); +SELECT next_day ('2003-08-06', 'SUNDAY'); +SELECT next_day ('2008-01-01', 'sun'); +SELECT next_day ('2008-01-01', 'sunAAA'); +SELECT next_day ('2008-01-01', 1); +SELECT next_day ('2008-01-01', 7); + +SET search_path TO oracle,"$user", public, pg_catalog; +SELECT next_day ('2003-08-01 111211', 'TUESDAY'); +SELECT next_day ('2003-08-06 10:11:43', 'WEDNESDAY'); +SELECT next_day ('2003-08-06 11:21:21', 'SUNDAY'); +SELECT next_day ('2008-01-01 111343', 'sun'); +SELECT next_day ('2008-01-01 121212', 'sunAAA'); +SELECT next_day ('2008-01-01 111213', 1); +SELECT next_day ('2008-01-01 11:12:13', 7); +SET search_path TO default; + +SELECT months_between (to_date ('2003/01/01', 'yyyy/mm/dd'), to_date ('2003/03/14', 'yyyy/mm/dd')); +SELECT months_between (to_date ('2003/07/01', 'yyyy/mm/dd'), to_date ('2003/03/14', 'yyyy/mm/dd')); +SELECT months_between (to_date ('2003/07/02', 'yyyy/mm/dd'), to_date ('2003/07/02', 'yyyy/mm/dd')); +SELECT months_between (to_date ('2003/08/02', 'yyyy/mm/dd'), to_date ('2003/06/02', 'yyyy/mm/dd')); +SELECT months_between ('2007-02-28', '2007-04-30'); +SELECT months_between ('2008-01-31', '2008-02-29'); +SELECT months_between ('2008-02-29', '2008-03-31'); +SELECT months_between ('2008-02-29', '2008-04-30'); +SELECT trunc(months_between('21-feb-2008', '2008-02-29')); + +SET search_path TO oracle,"$user", public, pg_catalog; +SELECT months_between (to_date ('2003/01/01 12:12:12', 'yyyy/mm/dd h24:mi:ss'), to_date ('2003/03/14 11:11:11', 'yyyy/mm/dd h24:mi:ss')); +SELECT months_between (to_date ('2003/07/01 10:11:11', 'yyyy/mm/dd h24:mi:ss'), to_date ('2003/03/14 10:12:12', 'yyyy/mm/dd h24:mi:ss')); +SELECT months_between (to_date ('2003/07/02 11:21:21', 'yyyy/mm/dd h24:mi:ss'), to_date ('2003/07/02 11:11:11', 'yyyy/mm/dd h24:mi:ss')); +SELECT months_between (to_timestamp ('2003/08/02 10:11:12', 'yyyy/mm/dd h24:mi:ss'), to_date ('2003/06/02 10:10:11', 'yyyy/mm/dd h24:mi:ss')); +SELECT months_between ('2007-02-28 111111', '2007-04-30 112121'); +SELECT months_between ('2008-01-31 11:32:11', '2008-02-29 11:12:12'); +SELECT months_between ('2008-02-29 10:11:13', '2008-03-31 10:12:11'); +SELECT months_between ('2008-02-29 111111', '2008-04-30 12:12:12'); +SELECT trunc(months_between('21-feb-2008 12:11:11', '2008-02-29 11:11:11')); +SET search_path TO default; + +select length('jmenuji se Pavel Stehule'),dbms_pipe.pack_message('jmenuji se Pavel Stehule'); +select length('a bydlim ve Skalici'),dbms_pipe.pack_message('a bydlim ve Skalici'); +select dbms_pipe.send_message('pavel',0,1); +select dbms_pipe.send_message('pavel',0,2); +select dbms_pipe.receive_message('pavel',0); +select '>>>>'||dbms_pipe.unpack_message_text()||'<<<<'; +select '>>>>'||dbms_pipe.unpack_message_text()||'<<<<'; +select dbms_pipe.receive_message('pavel',0); + +select dbms_pipe.purge('bob'); +select dbms_pipe.reset_buffer(); + +select dbms_pipe.pack_message('012345678901234+1'); +select dbms_pipe.send_message('bob',0,10); +select dbms_pipe.pack_message('012345678901234+2'); +select dbms_pipe.send_message('bob',0,10); +select dbms_pipe.pack_message('012345678901234+3'); +select dbms_pipe.send_message('bob',0,10); +-------------------------------------------- +select dbms_pipe.receive_message('bob',0); +select dbms_pipe.unpack_message_text(); +select dbms_pipe.receive_message('bob',0); +select dbms_pipe.unpack_message_text(); +select dbms_pipe.receive_message('bob',0); +select dbms_pipe.unpack_message_text(); + +select dbms_pipe.unique_session_name() LIKE 'PG$PIPE$%'; +select dbms_pipe.pack_message('012345678901234-1'); +select dbms_pipe.send_message('bob',0,10); +select dbms_pipe.receive_message('bob',0); +select dbms_pipe.unpack_message_text(); +select dbms_pipe.pack_message('012345678901234-2'); +select dbms_pipe.send_message('bob',0,10); +select dbms_pipe.send_message('bob',0,10); +select dbms_pipe.receive_message('bob',0); +select dbms_pipe.unpack_message_text(); + +select dbms_pipe.pack_message(TO_DATE('2006-10-11', 'YYYY-MM-DD')); +select dbms_pipe.send_message('test_date'); +select dbms_pipe.receive_message('test_date'); +select dbms_pipe.next_item_type(); +select dbms_pipe.unpack_message_date(); + +select dbms_pipe.pack_message(to_timestamp('2008-10-30 01:23:45', 'YYYY-MM-DD HH24:MI:SS')); +select dbms_pipe.send_message('test_timestamp'); +select dbms_pipe.receive_message('test_timestamp'); +select dbms_pipe.next_item_type(); +select to_char(dbms_pipe.unpack_message_timestamp(), 'YYYY-MM-DD HH24:MI:SS'); + +select dbms_pipe.pack_message(6262626262::numeric); +select dbms_pipe.send_message('test_int'); +select dbms_pipe.receive_message('test_int'); +select dbms_pipe.next_item_type(); +select dbms_pipe.unpack_message_number(); +select dbms_pipe.purge('bob'); + +select name, items, "limit", private, owner from dbms_pipe.db_pipes where name = 'bob'; + +select PLVstr.betwn('Harry and Sally are very happy', 7, 9); +select PLVstr.betwn('Harry and Sally are very happy', 7, 9, FALSE); +select PLVstr.betwn('Harry and Sally are very happy', -3, -1); +select PLVstr.betwn('Harry and Sally are very happy', 'a', 'ry'); +select PLVstr.betwn('Harry and Sally are very happy', 'a', 'ry', 1,1,FALSE,FALSE); +select PLVstr.betwn('Harry and Sally are very happy', 'a', 'ry', 2,1,TRUE,FALSE); +select PLVstr.betwn('Harry and Sally are very happy', 'a', 'y', 2,1); +select PLVstr.betwn('Harry and Sally are very happy', 'a', 'a', 2, 2); +select PLVstr.betwn('Harry and Sally are very happy', 'a', 'a', 2, 3, FALSE,FALSE); + +select plvsubst.string('My name is %s %s.', ARRAY['Pavel','Stěhule']); +select plvsubst.string('My name is % %.', ARRAY['Pavel','Stěhule'], '%'); +select plvsubst.string('My name is %s.', ARRAY['Stěhule']); +select plvsubst.string('My name is %s %s.', 'Pavel,Stěhule'); +select plvsubst.string('My name is %s %s.', 'Pavel|Stěhule','|'); +select plvsubst.string('My name is %s.', 'Stěhule'); +select plvsubst.string('My name is %s.', ''); +select plvsubst.string('My name is empty.', ''); + +select round(to_date ('22-AUG-03', 'DD-MON-YY'),'YEAR') = to_date ('01-JAN-04', 'DD-MON-YY'); +select round(to_date ('22-AUG-03', 'DD-MON-YY'),'Q') = to_date ('01-OCT-03', 'DD-MON-YY'); +select round(to_date ('22-AUG-03', 'DD-MON-YY'),'MONTH') = to_date ('01-SEP-03', 'DD-MON-YY'); +select round(to_date ('22-AUG-03', 'DD-MON-YY'),'DDD') = to_date ('22-AUG-03', 'DD-MON-YY'); +select round(to_date ('22-AUG-03', 'DD-MON-YY'),'DAY') = to_date ('24-AUG-03', 'DD-MON-YY'); +select trunc(to_date('22-AUG-03', 'DD-MON-YY'), 'YEAR') = to_date ('01-JAN-03', 'DD-MON-YY'); +select trunc(to_date('22-AUG-03', 'DD-MON-YY'), 'Q') = to_date ('01-JUL-03', 'DD-MON-YY'); +select trunc(to_date('22-AUG-03', 'DD-MON-YY'), 'MONTH') = to_date ('01-AUG-03', 'DD-MON-YY'); +select trunc(to_date('22-AUG-03', 'DD-MON-YY'), 'DDD') = to_date ('22-AUG-03', 'DD-MON-YY'); +select trunc(to_date('22-AUG-03', 'DD-MON-YY'), 'DAY') = to_date ('17-AUG-03', 'DD-MON-YY'); + +select trunc(TIMESTAMP WITH TIME ZONE '2004-10-19 10:23:54+02','YEAR') = '2004-01-01 00:00:00-08'; +select trunc(TIMESTAMP WITH TIME ZONE '2004-10-19 10:23:54+02','Q') = '2004-10-01 00:00:00-07'; +select trunc(TIMESTAMP WITH TIME ZONE '2004-10-19 10:23:54+02','MONTH') = '2004-10-01 00:00:00-07'; +select trunc(TIMESTAMP WITH TIME ZONE '2004-10-19 10:23:54+02','DDD') = '2004-10-19 00:00:00-07'; +select trunc(TIMESTAMP WITH TIME ZONE '2004-10-19 10:23:54+02','DAY') = '2004-10-17 00:00:00-07'; +select trunc(TIMESTAMP WITH TIME ZONE '2004-10-19 10:23:54+02','HH') = '2004-10-19 01:00:00-07'; +select trunc(TIMESTAMP WITH TIME ZONE '2004-10-19 10:23:54+02','MI') = '2004-10-19 01:23:00-07'; + +select next_day(to_date('01-Aug-03', 'DD-MON-YY'), 'TUESDAY') = to_date ('05-Aug-03', 'DD-MON-YY'); +select next_day(to_date('06-Aug-03', 'DD-MON-YY'), 'WEDNESDAY') = to_date ('13-Aug-03', 'DD-MON-YY'); +select next_day(to_date('06-Aug-03', 'DD-MON-YY'), 'SUNDAY') = to_date ('10-Aug-03', 'DD-MON-YY'); + +SET search_path TO oracle,"$user", public, pg_catalog; +select next_day(to_date('01-Aug-03 101111', 'DD-MON-YY h24miss'), 'TUESDAY') = to_date ('05-Aug-03 101111', 'DD-MON-YY h24miss'); +select next_day(to_date('06-Aug-03 10:12:13', 'DD-MON-YY H24:MI:SS'), 'WEDNESDAY') = to_date ('13-Aug-03 10:12:13', 'DD-MON-YY H24:MI:SS'); +select next_day(to_date('06-Aug-03 11:11:11', 'DD-MON-YY HH:MI:SS'), 'SUNDAY') = to_date ('10-Aug-03 11:11:11', 'DD-MON-YY HH:MI:SS'); +SET search_path TO default; + +select instr('Tech on the net', 'e') =2; +select instr('Tech on the net', 'e', 1, 1) = 2; +select instr('Tech on the net', 'e', 1, 2) = 11; +select instr('Tech on the net', 'e', 1, 3) = 14; +select instr('Tech on the net', 'e', -3, 2) = 2; +select instr('abc', NULL) IS NULL; +select 1 = instr('abc', ''); +select 1 = instr('abc', 'a'); +select 3 = instr('abc', 'c'); +select 0 = instr('abc', 'z'); +select 1 = instr('abcabcabc', 'abca', 1); +select 4 = instr('abcabcabc', 'abca', 2); +select 0 = instr('abcabcabc', 'abca', 7); +select 0 = instr('abcabcabc', 'abca', 9); +select 4 = instr('abcabcabc', 'abca', -1); +select 1 = instr('abcabcabc', 'abca', -8); +select 1 = instr('abcabcabc', 'abca', -9); +select 0 = instr('abcabcabc', 'abca', -10); +select 1 = instr('abcabcabc', 'abca', 1, 1); +select 4 = instr('abcabcabc', 'abca', 1, 2); +select 0 = instr('abcabcabc', 'abca', 1, 3); +select oracle.substr('This is a test', 6, 2) = 'is'; +select oracle.substr('This is a test', 6) = 'is a test'; +select oracle.substr('TechOnTheNet', 1, 4) = 'Tech'; +select oracle.substr('TechOnTheNet', -3, 3) = 'Net'; +select oracle.substr('TechOnTheNet', -6, 3) = 'The'; +select oracle.substr('TechOnTheNet', -8, 2) = 'On'; +select oracle.substr('TechOnTheNet', -8, 0) = ''; +select oracle.substr('TechOnTheNet', -8, -1) = ''; +select oracle.substr(1234567,3.6::smallint)='4567'; +select oracle.substr(1234567,3.6::int)='4567'; +select oracle.substr(1234567,3.6::bigint)='4567'; +select oracle.substr(1234567,3.6::numeric)='34567'; +select oracle.substr(1234567,-1)='7'; +select oracle.substr(1234567,3.6::smallint,2.6)='45'; +select oracle.substr(1234567,3.6::smallint,2.6::smallint)='456'; +select oracle.substr(1234567,3.6::smallint,2.6::int)='456'; +select oracle.substr(1234567,3.6::smallint,2.6::bigint)='456'; +select oracle.substr(1234567,3.6::smallint,2.6::numeric)='45'; +select oracle.substr(1234567,3.6::int,2.6::smallint)='456'; +select oracle.substr(1234567,3.6::int,2.6::int)='456'; +select oracle.substr(1234567,3.6::int,2.6::bigint)='456'; +select oracle.substr(1234567,3.6::int,2.6::numeric)='45'; +select oracle.substr(1234567,3.6::bigint,2.6::smallint)='456'; +select oracle.substr(1234567,3.6::bigint,2.6::int)='456'; +select oracle.substr(1234567,3.6::bigint,2.6::bigint)='456'; +select oracle.substr(1234567,3.6::bigint,2.6::numeric)='45'; +select oracle.substr(1234567,3.6::numeric,2.6::smallint)='345'; +select oracle.substr(1234567,3.6::numeric,2.6::int)='345'; +select oracle.substr(1234567,3.6::numeric,2.6::bigint)='345'; +select oracle.substr(1234567,3.6::numeric,2.6::numeric)='34'; +select oracle.substr('abcdef'::varchar,3.6::smallint)='def'; +select oracle.substr('abcdef'::varchar,3.6::int)='def'; +select oracle.substr('abcdef'::varchar,3.6::bigint)='def'; +select oracle.substr('abcdef'::varchar,3.6::numeric)='cdef'; +select oracle.substr('abcdef'::varchar,3.5::int,3.5::int)='def'; +select oracle.substr('abcdef'::varchar,3.5::numeric,3.5::numeric)='cde'; +select oracle.substr('abcdef'::varchar,3.5::numeric,3.5::int)='cdef'; +select concat('Tech on', ' the Net') = 'Tech on the Net'; +select concat('a', 'b') = 'ab'; +select concat('a', NULL) = 'a'; +select concat(NULL, 'b') = 'b'; +select concat('a', 2) = 'a2'; +select concat(1, 'b') = '1b'; +select concat(1, 2) = '12'; +select concat(1, NULL) = '1'; +select concat(NULL, 2) = '2'; +select nvl('A'::text, 'B'); +select nvl(NULL::text, 'B'); +select nvl(NULL::text, NULL); +select nvl(1, 2); +select nvl(NULL, 2); +select nvl2('A'::text, 'B', 'C'); +select nvl2(NULL::text, 'B', 'C'); +select nvl2('A'::text, NULL, 'C'); +select nvl2(NULL::text, 'B', NULL); +select nvl2(1, 2, 3); +select nvl2(NULL, 2, 3); +select lnnvl(true); +select lnnvl(false); +select lnnvl(NULL); +select decode(1, 1, 100, 2, 200); +select decode(2, 1, 100, 2, 200); +select decode(3, 1, 100, 2, 200); +select decode(3, 1, 100, 2, 200, 300); +select decode(NULL, 1, 100, NULL, 200, 300); +select decode('1'::text, '1', 100, '2', 200); +select decode(2, 1, 'ABC', 2, 'DEF'); +select decode('2009-02-05'::date, '2009-02-05', 'ok'); +select decode('2009-02-05 01:02:03'::timestamp, '2009-02-05 01:02:03', 'ok'); + +-- For type 'bpchar' +select decode('a'::bpchar, 'a'::bpchar,'postgres'::bpchar); +select decode('c'::bpchar, 'a'::bpchar,'postgres'::bpchar); +select decode('a'::bpchar, 'a'::bpchar,'postgres'::bpchar,'default value'::bpchar); +select decode('c', 'a'::bpchar,'postgres'::bpchar,'default value'::bpchar); + +select decode('a'::bpchar, 'a'::bpchar,'postgres'::bpchar,'b'::bpchar,'database'::bpchar); +select decode('d'::bpchar, 'a'::bpchar,'postgres'::bpchar,'b'::bpchar,'database'::bpchar); +select decode('a'::bpchar, 'a'::bpchar,'postgres'::bpchar,'b'::bpchar,'database'::bpchar,'default value'::bpchar); +select decode('d'::bpchar, 'a'::bpchar,'postgres'::bpchar,'b'::bpchar,'database'::bpchar,'default value'::bpchar); + +select decode('a'::bpchar, 'a'::bpchar,'postgres'::bpchar,'b'::bpchar,'database'::bpchar, 'c'::bpchar, 'system'::bpchar); +select decode('d'::bpchar, 'a'::bpchar,'postgres'::bpchar,'b'::bpchar,'database'::bpchar, 'c'::bpchar, 'system'::bpchar); +select decode('a'::bpchar, 'a'::bpchar,'postgres'::bpchar,'b'::bpchar,'database'::bpchar, 'c'::bpchar, 'system'::bpchar,'default value'::bpchar); +select decode('d'::bpchar, 'a'::bpchar,'postgres'::bpchar,'b'::bpchar,'database'::bpchar, 'c'::bpchar, 'system'::bpchar,'default value'::bpchar); + +select decode(NULL, 'a'::bpchar, 'postgres'::bpchar, NULL,'database'::bpchar); +select decode(NULL, 'a'::bpchar, 'postgres'::bpchar, 'b'::bpchar,'database'::bpchar); +select decode(NULL, 'a'::bpchar, 'postgres'::bpchar, NULL,'database'::bpchar,'default value'::bpchar); +select decode(NULL, 'a'::bpchar, 'postgres'::bpchar, 'b'::bpchar,'database'::bpchar,'default value'::bpchar); + +-- For type 'bigint' +select decode(2147483651::bigint, 2147483650::bigint,2147483650::bigint); +select decode(2147483653::bigint, 2147483651::bigint,2147483650::bigint); +select decode(2147483653::bigint, 2147483651::bigint,2147483650::bigint,9999999999::bigint); +select decode(2147483653::bigint, 2147483651::bigint,2147483650::bigint,9999999999::bigint); + +select decode(2147483651::bigint, 2147483651::bigint,2147483650::bigint,2147483652::bigint,2147483651::bigint); +select decode(2147483654::bigint, 2147483651::bigint,2147483650::bigint,2147483652::bigint,2147483651::bigint); +select decode(2147483651::bigint, 2147483651::bigint,2147483650::bigint,2147483652::bigint,2147483651::bigint,9999999999::bigint); +select decode(2147483654::bigint, 2147483651::bigint,2147483650::bigint,2147483652::bigint,2147483651::bigint,9999999999::bigint); + +select decode(2147483651::bigint, 2147483651::bigint,2147483650::bigint, 2147483652::bigint,2147483651::bigint, 2147483653::bigint, 2147483652::bigint); +select decode(2147483654::bigint, 2147483651::bigint,2147483650::bigint, 2147483652::bigint,2147483651::bigint, 2147483653::bigint, 2147483652::bigint); +select decode(2147483651::bigint, 2147483651::bigint,2147483650::bigint, 2147483652::bigint,2147483651::bigint, 2147483653::bigint, 2147483652::bigint,9999999999::bigint); +select decode(2147483654::bigint, 2147483651::bigint,2147483650::bigint, 2147483652::bigint,2147483651::bigint, 2147483653::bigint, 2147483652::bigint,9999999999::bigint); + +select decode(NULL, 2147483651::bigint, 2147483650::bigint, NULL,2147483651::bigint); +select decode(NULL, 2147483651::bigint, 2147483650::bigint, 2147483652::bigint,2147483651::bigint); +select decode(NULL, 2147483651::bigint, 2147483650::bigint, NULL,2147483651::bigint,9999999999::bigint); +select decode(NULL, 2147483651::bigint, 2147483650::bigint, 2147483652::bigint,2147483651::bigint,9999999999::bigint); + +-- For type 'numeric' +select decode(12.001::numeric(5,3), 12.001::numeric(5,3),214748.3650::numeric(10,4)); +select decode(12.003::numeric(5,3), 12.001::numeric(5,3),214748.3650::numeric(10,4)); +select decode(12.001::numeric(5,3), 12.001::numeric(5,3),214748.3650::numeric(10,4),999999.9999::numeric(10,4)); +select decode(12.003::numeric(5,3), 12.001::numeric(5,3),214748.3650::numeric(10,4),999999.9999::numeric(10,4)); + +select decode(12.001::numeric(5,3), 12.001::numeric(5,3),214748.3650::numeric(10,4),12.002::numeric(5,3),214748.3651::numeric(10,4)); +select decode(12.004::numeric(5,3), 12.001::numeric(5,3),214748.3650::numeric(10,4),12.002::numeric(5,3),214748.3651::numeric(10,4)); +select decode(12.001::numeric(5,3), 12.001::numeric(5,3),214748.3650::numeric(10,4),12.002::numeric(5,3),214748.3651::numeric(10,4),999999.9999::numeric(10,4)); +select decode(12.004::numeric(5,3), 12.001::numeric(5,3),214748.3650::numeric(10,4),12.002::numeric(5,3),214748.3651::numeric(10,4),999999.9999::numeric(10,4)); + +select decode(12.001::numeric(5,3), 12.001::numeric(5,3),214748.3650::numeric(10,4),12.002::numeric(5,3),214748.3651::numeric(10,4), 12.003::numeric(5,3), 214748.3652::numeric(10,4)); +select decode(12.004::numeric(5,3), 12.001::numeric(5,3),214748.3650::numeric(10,4),12.002::numeric(5,3),214748.3651::numeric(10,4), 12.003::numeric(5,3), 214748.3652::numeric(10,4)); +select decode(12.001::numeric(5,3), 12.001::numeric(5,3),214748.3650::numeric(10,4),12.002::numeric(5,3),214748.3651::numeric(10,4), 12.003::numeric(5,3), 214748.3652::numeric(10,4),999999.9999::numeric(10,4)); +select decode(12.004::numeric(5,3), 12.001::numeric(5,3),214748.3650::numeric(10,4),12.002::numeric(5,3),214748.3651::numeric(10,4), 12.003::numeric(5,3), 214748.3652::numeric(10,4),999999.9999::numeric(10,4)); + +select decode(NULL, 12.001::numeric(5,3), 214748.3650::numeric(10,4), NULL,214748.3651::numeric(10,4)); +select decode(NULL, 12.001::numeric(5,3), 214748.3650::numeric(10,4), 12.002::numeric(5,3),214748.3651::numeric(10,4)); +select decode(NULL, 12.001::numeric(5,3), 214748.3650::numeric(10,4), NULL,214748.3651::numeric(10,4),999999.9999::numeric(10,4)); +select decode(NULL, 12.001::numeric(5,3), 214748.3650::numeric(10,4), 12.002::numeric(5,3),214748.3651::numeric(10,4),999999.9999::numeric(10,4)); + +--For type 'date' +select decode('2020-01-01'::date, '2020-01-01'::date,'2012-12-20'::date); +select decode('2020-01-03'::date, '2020-01-01'::date,'2012-12-20'::date); +select decode('2020-01-01'::date, '2020-01-01'::date,'2012-12-20'::date,'2012-12-21'::date); +select decode('2020-01-03'::date, '2020-01-01'::date,'2012-12-20'::date,'2012-12-21'::date); + +select decode('2020-01-01'::date, '2020-01-01'::date,'2012-12-20'::date,'2020-01-02'::date,'2012-12-21'::date); +select decode('2020-01-04'::date, '2020-01-01'::date,'2012-12-20'::date,'2020-01-02'::date,'2012-12-21'::date); +select decode('2020-01-01'::date, '2020-01-01'::date,'2012-12-20'::date,'2020-01-02'::date,'2012-12-21'::date,'2012-12-31'::date); +select decode('2020-01-04'::date, '2020-01-01'::date,'2012-12-20'::date,'2020-01-02'::date,'2012-12-21'::date,'2012-12-31'::date); + +select decode('2020-01-01'::date, '2020-01-01'::date,'2012-12-20'::date,'2020-01-02'::date,'2012-12-21'::date, '2020-01-03'::date, '2012-12-31'::date); +select decode('2020-01-04'::date, '2020-01-01'::date,'2012-12-20'::date,'2020-01-02'::date,'2012-12-21'::date, '2020-01-03'::date, '2012-12-31'::date); +select decode('2020-01-01'::date, '2020-01-01'::date,'2012-12-20'::date,'2020-01-02'::date,'2012-12-21'::date, '2020-01-03'::date, '2012-12-31'::date,'2013-01-01'::date); +select decode('2020-01-04'::date, '2020-01-01'::date,'2012-12-20'::date,'2020-01-02'::date,'2012-12-21'::date, '2020-01-03'::date, '2012-12-31'::date,'2013-01-01'::date); + +select decode(NULL, '2020-01-01'::date, '2012-12-20'::date, NULL,'2012-12-21'::date); +select decode(NULL, '2020-01-01'::date, '2012-12-20'::date, '2020-01-02'::date,'2012-12-21'::date); +select decode(NULL, '2020-01-01'::date, '2012-12-20'::date, NULL,'2012-12-21'::date,'2012-12-31'::date); +select decode(NULL, '2020-01-01'::date, '2012-12-20'::date, '2020-01-02'::date,'2012-12-21'::date,'2012-12-31'::date); + +-- For type 'time' +select decode('01:00:01'::time, '01:00:01'::time,'09:00:00'::time); +select decode('01:00:03'::time, '01:00:01'::time,'09:00:00'::time); +select decode('01:00:01'::time, '01:00:01'::time,'09:00:00'::time,'00:00:00'::time); +select decode('01:00:03'::time, '01:00:01'::time,'09:00:00'::time,'00:00:00'::time); + +select decode('01:00:01'::time, '01:00:01'::time,'09:00:00'::time,'01:00:02'::time,'12:00:00'::time); +select decode('01:00:04'::time, '01:00:01'::time,'09:00:00'::time,'01:00:02'::time,'12:00:00'::time); +select decode('01:00:01'::time, '01:00:01'::time,'09:00:00'::time,'01:00:02'::time,'12:00:00'::time,'00:00:00'::time); +select decode('01:00:04'::time, '01:00:01'::time,'09:00:00'::time,'01:00:01'::time,'12:00:00'::time,'00:00:00'::time); + +select decode('01:00:01'::time, '01:00:01'::time,'09:00:00'::time,'01:00:02'::time,'12:00:00'::time, '01:00:03'::time, '15:00:00'::time); +select decode('01:00:04'::time, '01:00:01'::time,'09:00:00'::time,'01:00:02'::time,'12:00:00'::time, '01:00:03'::time, '15:00:00'::time); +select decode('01:00:01'::time, '01:00:01'::time,'09:00:00'::time,'01:00:02'::time,'12:00:00'::time, '01:00:03'::time, '15:00:00'::time,'00:00:00'::time); +select decode('01:00:04'::time, '01:00:01'::time,'09:00:00'::time,'01:00:02'::time,'12:00:00'::time, '01:00:03'::time, '15:00:00'::time,'00:00:00'::time); + +select decode(NULL, '01:00:01'::time, '09:00:00'::time, NULL,'12:00:00'::time); +select decode(NULL, '01:00:01'::time, '09:00:00'::time, '01:00:02'::time,'12:00:00'::time); +select decode(NULL, '01:00:01'::time, '09:00:00'::time, NULL,'12:00:00'::time,'00:00:00'::time); +select decode(NULL, '01:00:01'::time, '09:00:00'::time, '01:00:02'::time,'12:00:00'::time,'00:00:00'::time); + +-- For type 'timestamp' +select decode('2020-01-01 01:00:01'::timestamp, '2020-01-01 01:00:01'::timestamp,'2012-12-20 09:00:00'::timestamp); +select decode('2020-01-03 01:00:01'::timestamp, '2020-01-01 01:00:01'::timestamp,'2012-12-20 09:00:00'::timestamp); +select decode('2020-01-01 01:00:01'::timestamp, '2020-01-01 01:00:01'::timestamp,'2012-12-20 09:00:00'::timestamp,'2012-12-20 00:00:00'::timestamp); +select decode('2020-01-03 01:00:01'::timestamp, '2020-01-01 01:00:01'::timestamp,'2012-12-20 09:00:00'::timestamp,'2012-12-20 00:00:00'::timestamp); + +select decode('2020-01-01 01:00:01'::timestamp, '2020-01-01 01:00:01'::timestamp,'2012-12-20 09:00:00'::timestamp,'2020-01-02 01:00:01'::timestamp,'2012-12-20 12:00:00'::timestamp); +select decode('2020-01-04 01:00:01'::timestamp, '2020-01-01 01:00:01'::timestamp,'2012-12-20 09:00:00'::timestamp,'2020-01-02 01:00:01'::timestamp,'2012-12-20 12:00:00'::timestamp); +select decode('2020-01-01 01:00:01'::timestamp, '2020-01-01 01:00:01'::timestamp,'2012-12-20 09:00:00'::timestamp,'2020-01-02 01:00:01'::timestamp,'2012-12-20 12:00:00'::timestamp,'2012-12-20 00:00:00'::timestamp); +select decode('2020-01-04 01:00:01'::timestamp, '2020-01-01 01:00:01'::timestamp,'2012-12-20 09:00:00'::timestamp,'2020-01-02 01:00:01'::timestamp,'2012-12-20 12:00:00'::timestamp,'2012-12-20 00:00:00'::timestamp); + +select decode('2020-01-01 01:00:01'::timestamp, '2020-01-01 01:00:01'::timestamp,'2012-12-20 09:00:00'::timestamp,'2020-01-02 01:00:01'::timestamp,'2012-12-20 12:00:00'::timestamp, '2020-01-03 01:00:01'::timestamp, '2012-12-20 15:00:00'::timestamp); +select decode('2020-01-04 01:00:01'::timestamp, '2020-01-01 01:00:01'::timestamp,'2012-12-20 09:00:00'::timestamp,'2020-01-02 01:00:01'::timestamp,'2012-12-20 12:00:00'::timestamp, '2020-01-03 01:00:01'::timestamp, '2012-12-20 15:00:00'::timestamp); +select decode('2020-01-01 01:00:01'::timestamp, '2020-01-01 01:00:01'::timestamp,'2012-12-20 09:00:00'::timestamp,'2020-01-02 01:00:01'::timestamp,'2012-12-20 12:00:00'::timestamp, '2020-01-03 01:00:01'::timestamp, '2012-12-20 15:00:00'::timestamp,'2012-12-20 00:00:00'::timestamp); +select decode('2020-01-04 01:00:01'::timestamp, '2020-01-01 01:00:01'::timestamp,'2012-12-20 09:00:00'::timestamp,'2020-01-02 01:00:01'::timestamp,'2012-12-20 12:00:00'::timestamp, '2020-01-03 01:00:01'::timestamp, '2012-12-20 15:00:00'::timestamp,'2012-12-20 00:00:00'::timestamp); + +select decode(NULL, '2020-01-01 01:00:01'::timestamp, '2012-12-20 09:00:00'::timestamp, NULL,'2012-12-20 12:00:00'::timestamp); +select decode(NULL, '2020-01-01 01:00:01'::timestamp, '2012-12-20 09:00:00'::timestamp, '2020-01-02 01:00:01'::timestamp,'2012-12-20 12:00:00'::timestamp); +select decode(NULL, '2020-01-01 01:00:01'::timestamp, '2012-12-20 09:00:00'::timestamp, NULL,'2012-12-20 12:00:00'::timestamp,'2012-12-20 00:00:00'::timestamp); +select decode(NULL, '2020-01-01 01:00:01'::timestamp, '2012-12-20 09:00:00'::timestamp, '2020-01-02 01:00:01'::timestamp,'2012-12-20 12:00:00'::timestamp,'2012-12-20 00:00:00'::timestamp); + +-- For type 'timestamptz' +select decode('2020-01-01 01:00:01-08'::timestamptz, '2020-01-01 01:00:01-08'::timestamptz,'2012-12-20 09:00:00-08'::timestamptz); +select decode('2020-01-03 01:00:01-08'::timestamptz, '2020-01-01 01:00:01-08'::timestamptz,'2012-12-20 09:00:00-08'::timestamptz); +select decode('2020-01-01 01:00:01-08'::timestamptz, '2020-01-01 01:00:01-08'::timestamptz,'2012-12-20 09:00:00-08'::timestamptz,'2012-12-20 00:00:00-08'::timestamptz); +select decode('2020-01-03 01:00:01-08'::timestamptz, '2020-01-01 01:00:01-08'::timestamptz,'2012-12-20 09:00:00-08'::timestamptz,'2012-12-20 00:00:00-08'::timestamptz); + +select decode('2020-01-01 01:00:01-08'::timestamptz, '2020-01-01 01:00:01-08'::timestamptz,'2012-12-20 09:00:00-08'::timestamptz,'2020-01-02 01:00:01-08'::timestamptz,'2012-12-20 12:00:00-08'::timestamptz); +select decode('2020-01-04 01:00:01-08'::timestamptz, '2020-01-01 01:00:01-08'::timestamptz,'2012-12-20 09:00:00-08'::timestamptz,'2020-01-02 01:00:01-08'::timestamptz,'2012-12-20 12:00:00-08'::timestamptz); +select decode('2020-01-01 01:00:01-08'::timestamptz, '2020-01-01 01:00:01-08'::timestamptz,'2012-12-20 09:00:00-08'::timestamptz,'2020-01-02 01:00:01-08'::timestamptz,'2012-12-20 12:00:00-08'::timestamptz,'2012-12-20 00:00:00-08'::timestamptz); +select decode('2020-01-04 01:00:01-08'::timestamptz, '2020-01-01 01:00:01-08'::timestamptz,'2012-12-20 09:00:00-08'::timestamptz,'2020-01-02 01:00:01-08'::timestamptz,'2012-12-20 12:00:00-08'::timestamptz,'2012-12-20 00:00:00-08'::timestamptz); + +select decode('2020-01-01 01:00:01-08'::timestamptz, '2020-01-01 01:00:01-08'::timestamptz,'2012-12-20 09:00:00-08'::timestamptz,'2020-01-02 01:00:01-08'::timestamptz,'2012-12-20 12:00:00-08'::timestamptz, '2020-01-03 01:00:01-08'::timestamptz, '2012-12-20 15:00:00-08'::timestamptz); +select decode('2020-01-04 01:00:01-08'::timestamptz, '2020-01-01 01:00:01-08'::timestamptz,'2012-12-20 09:00:00-08'::timestamptz,'2020-01-02 01:00:01-08'::timestamptz,'2012-12-20 12:00:00-08'::timestamptz, '2020-01-03 01:00:01-08'::timestamptz, '2012-12-20 15:00:00-08'::timestamptz); +select decode('2020-01-01 01:00:01-08'::timestamptz, '2020-01-01 01:00:01-08'::timestamptz,'2012-12-20 09:00:00-08'::timestamptz,'2020-01-02 01:00:01-08'::timestamptz,'2012-12-20 12:00:00-08'::timestamptz, '2020-01-03 01:00:01-08'::timestamptz, '2012-12-20 15:00:00-08'::timestamptz,'2012-12-20 00:00:00-08'::timestamptz); +select decode(4, 1,'2012-12-20 09:00:00-08'::timestamptz,2,'2012-12-20 12:00:00-08'::timestamptz, 3, '2012-12-20 15:00:00-08'::timestamptz,'2012-12-20 00:00:00-08'::timestamptz); + +select decode(NULL, '2020-01-01 01:00:01-08'::timestamptz, '2012-12-20 09:00:00-08'::timestamptz, NULL,'2012-12-20 12:00:00-08'::timestamptz); +select decode(NULL, '2020-01-01 01:00:01-08'::timestamptz, '2012-12-20 09:00:00-08'::timestamptz, '2020-01-02 01:00:01-08'::timestamptz,'2012-12-20 12:00:00-08'::timestamptz); +select decode(NULL, '2020-01-01 01:00:01-08'::timestamptz, '2012-12-20 09:00:00-08'::timestamptz, NULL,'2012-12-20 12:00:00-08'::timestamptz,'2012-12-20 00:00:00-08'::timestamptz); +select decode(NULL, '2020-01-01 01:00:01-08'::timestamptz, '2012-12-20 09:00:00-08'::timestamptz, '2020-01-02 01:00:01-08'::timestamptz,'2012-12-20 12:00:00-08'::timestamptz,'2012-12-20 00:00:00-08'::timestamptz); + + +--Test case to check if decode accepts other expressions as a key + +CREATE OR REPLACE FUNCTION five() RETURNS integer AS $$ +BEGIN + RETURN 5; +END; +$$ LANGUAGE plpgsql; + +select decode(five(), 1, 'one', 2, 'two', 5, 'five'); + +DROP FUNCTION five(); + +-- Test case to check duplicate keys in search list +select decode(1, 1, 'one', 2, 'two', 1, 'one-again') = 'one'; + +/* Test case to check explicit type casting of keys in search list in + * case of ambiguous key (1st argument) provided. + */ + +-- 1) succeed and return 'result-1' +select decode('2012-01-01', '2012-01-01'::date,'result-1','2012-01-02', 'result-2'); +select decode('2012-01-01', '2012-01-01', 'result-1', '2012-02-01'::date, 'result-2'); + +select PLVstr.rvrs ('Jumping Jack Flash') ='hsalF kcaJ gnipmuJ'; +select PLVstr.rvrs ('Jumping Jack Flash', 9) = 'hsalF kcaJ'; +select PLVstr.rvrs ('Jumping Jack Flash', 4, 6) = 'nip'; +select PLVstr.rvrs (NULL, 10, 20); +select PLVstr.rvrs ('alphabet', -2, -5); +select PLVstr.rvrs ('alphabet', -2); +select PLVstr.rvrs ('alphabet', 2, 200); +select PLVstr.rvrs ('alphabet', 20, 200); +select PLVstr.lstrip ('*val1|val2|val3|*', '*') = 'val1|val2|val3|*'; +select PLVstr.lstrip (',,,val1,val2,val3,', ',', 3)= 'val1,val2,val3,'; +select PLVstr.lstrip ('WHERE WHITE = ''FRONT'' AND COMP# = 1500', 'WHERE ') = 'WHITE = ''FRONT'' AND COMP# = 1500'; +select plvstr.left('Příliš žluťoučký kůň',4) = pg_catalog.substr('Příl', 1, 4); + +select pos,token from plvlex.tokens('select * from a.b.c join d ON x=y', true, true); + +SET lc_numeric TO 'C'; +select to_char(22); +select to_char(99::smallint); +select to_char(-44444); +select to_char(1234567890123456::bigint); +select to_char(123.456::real); +select to_char(1234.5678::double precision); +select to_char(12345678901234567890::numeric); +select to_char(1234567890.12345); +select to_char('4.00'::numeric); +select to_char('4.0010'::numeric); + +SELECT to_number('123'::text); +SELECT to_number('123.456'::text); +SELECT to_number(123); +SELECT to_number(123::smallint); +SELECT to_number(123::int); +SELECT to_number(123::bigint); +SELECT to_number(123::numeric); +SELECT to_number(123.456); +SELECT to_number(1210.73, 9999.99); +SELECT to_number(1210::smallint, 9999::smallint); +SELECT to_number(1210::int, 9999::int); +SELECT to_number(1210::bigint, 9999::bigint); +SELECT to_number(1210.73::numeric, 9999.99::numeric); + +SELECT to_date('2009-01-02'); + +SELECT bitand(5,1), bitand(5,2), bitand(5,4); +SELECT sinh(1.570796)::numeric(10, 8), cosh(1.570796)::numeric(10, 8), tanh(4)::numeric(10, 8); +SELECT nanvl(12345, 1), nanvl('NaN', 1); +SELECT nanvl(12345::float4, 1), nanvl('NaN'::float4, 1); +SELECT nanvl(12345::float8, 1), nanvl('NaN'::float8, 1); +SELECT nanvl(12345::numeric, 1), nanvl('NaN'::numeric, 1); +SELECT nanvl(12345, '1'::varchar), nanvl('NaN', 1::varchar); +SELECT nanvl(12345::float4, '1'::varchar), nanvl('NaN'::float4, '1'::varchar); +SELECT nanvl(12345::float8, '1'::varchar), nanvl('NaN'::float8, '1'::varchar); +SELECT nanvl(12345::numeric, '1'::varchar), nanvl('NaN'::numeric, '1'::varchar); +SELECT nanvl(12345, '1'::char), nanvl('NaN', 1::char); +SELECT nanvl(12345::float4, '1'::char), nanvl('NaN'::float4, '1'::char); +SELECT nanvl(12345::float8, '1'::char), nanvl('NaN'::float8, '1'::char); +SELECT nanvl(12345::numeric, '1'::char), nanvl('NaN'::numeric, '1'::char); + +select dbms_assert.enquote_literal('some text '' some text'); +select dbms_assert.enquote_name('''"AAA'); +select dbms_assert.enquote_name('''"AAA', false); +select dbms_assert.noop('some string'); +select dbms_assert.qualified_sql_name('aaa.bbb.ccc."aaaa""aaa"'); +select dbms_assert.qualified_sql_name('aaa.bbb.cc%c."aaaa""aaa"'); +select dbms_assert.schema_name('dbms_assert'); +select dbms_assert.schema_name('jabadabado'); +select dbms_assert.simple_sql_name('"Aaa dghh shsh"'); +select dbms_assert.simple_sql_name('ajajaj -- ajaj'); +select dbms_assert.object_name('pg_catalog.pg_class'); +select dbms_assert.object_name('dbms_assert.fooo'); + +select dbms_assert.enquote_literal(NULL); +select dbms_assert.enquote_name(NULL); +select dbms_assert.enquote_name(NULL, false); +select dbms_assert.noop(NULL); +select dbms_assert.qualified_sql_name(NULL); +select dbms_assert.qualified_sql_name(NULL); +select dbms_assert.schema_name(NULL); +select dbms_assert.schema_name(NULL); +select dbms_assert.simple_sql_name(NULL); +select dbms_assert.simple_sql_name(NULL); +select dbms_assert.object_name(NULL); +select dbms_assert.object_name(NULL); + +select plunit.assert_true(NULL); +select plunit.assert_true(1 = 2); +select plunit.assert_true(1 = 2, 'one is not two'); +select plunit.assert_true(1 = 1); +select plunit.assert_false(1 = 1); +select plunit.assert_false(1 = 1, 'trap is open'); +select plunit.assert_false(NULL); +select plunit.assert_null(current_date); +select plunit.assert_null(NULL::date); +select plunit.assert_not_null(current_date); +select plunit.assert_not_null(NULL::date); +select plunit.assert_equals('Pavel','Pa'||'vel'); +select plunit.assert_equals(current_date, current_date + 1, 'diff dates'); +select plunit.assert_equals(10.2, 10.3, 0.5); +select plunit.assert_equals(10.2, 10.3, 0.01, 'attention some diff'); +select plunit.assert_not_equals(current_date, current_date + 1, 'yestarday is today'); +select plunit.fail(); +select plunit.fail('custom exception'); + +SELECT dump('Yellow dog'::text) ~ E'^Typ=25 Len=(\\d+): \\d+(,\\d+)*$' AS t; +SELECT dump('Yellow dog'::text, 10) ~ E'^Typ=25 Len=(\\d+): \\d+(,\\d+)*$' AS t; +SELECT dump('Yellow dog'::text, 17) ~ E'^Typ=25 Len=(\\d+): .(,.)*$' AS t; +SELECT dump(10::int2) ~ E'^Typ=21 Len=2: \\d+(,\\d+){1}$' AS t; +SELECT dump(10::int4) ~ E'^Typ=23 Len=4: \\d+(,\\d+){3}$' AS t; +SELECT dump(10::int8) ~ E'^Typ=20 Len=8: \\d+(,\\d+){7}$' AS t; +SELECT dump(10.23::float4) ~ E'^Typ=700 Len=4: \\d+(,\\d+){3}$' AS t; +SELECT dump(10.23::float8) ~ E'^Typ=701 Len=8: \\d+(,\\d+){7}$' AS t; +SELECT dump(10.23::numeric) ~ E'^Typ=1700 Len=(\\d+): \\d+(,\\d+)*$' AS t; +SELECT dump('2008-10-10'::date) ~ E'^Typ=1082 Len=4: \\d+(,\\d+){3}$' AS t; +SELECT dump('2008-10-10'::timestamp) ~ E'^Typ=1114 Len=8: \\d+(,\\d+){7}$' AS t; +SELECT dump('2009-10-10'::timestamp) ~ E'^Typ=1114 Len=8: \\d+(,\\d+){7}$' AS t; + +-- Tests for to_multi_byte +SELECT to_multi_byte('123$test'); +-- Check internal representation difference +SELECT octet_length('abc'); +SELECT octet_length(to_multi_byte('abc')); + +-- Tests for to_single_byte +SELECT to_single_byte('123$test'); +SELECT to_single_byte('123$test'); +-- Check internal representation difference +SELECT octet_length('abc'); +SELECT octet_length(to_single_byte('abc')); + +-- Tests for round(TIMESTAMP WITH TIME ZONE) +select round(TIMESTAMP WITH TIME ZONE'12/08/1990 05:35:25','YEAR') = '1991-01-01 00:00:00'; +select round(TIMESTAMP WITH TIME ZONE'05/08/1990 05:35:25','Q') = '1990-04-01 00:00:00'; +select round(TIMESTAMP WITH TIME ZONE'12/08/1990 05:35:25','MONTH') = '1990-12-01 00:00:00'; +select round(TIMESTAMP WITH TIME ZONE'12/08/1990 05:35:25','DDD') = '1990-12-08 00:00:00'; +select round(TIMESTAMP WITH TIME ZONE'12/08/1990 05:35:25','DAY') = '1990-12-09 00:00:00'; +select round(TIMESTAMP WITH TIME ZONE'12/08/1990 05:35:25','hh') = '1990-12-08 06:00:00'; +select round(TIMESTAMP WITH TIME ZONE'12/08/1990 05:35:25','mi') = '1990-12-08 05:35:00'; + +-- Tests for to_date +SET DATESTYLE TO SQL, MDY; +SELECT to_date('2009-01-02'); +select to_date('January 8,1999'); +SET DATESTYLE TO POSTGRES, MDY; +select to_date('1999-01-08'); +select to_date('1/12/1999'); +SET DATESTYLE TO SQL, DMY; +select to_date('01/02/03'); +select to_date('1999-Jan-08'); +select to_date('Jan-08-1999'); +select to_date('08-Jan-1999'); +SET DATESTYLE TO ISO, YMD; +select to_date('99-Jan-08'); +SET DATESTYLE TO ISO, DMY; +select to_date('08-Jan-99'); +select to_date('Jan-08-99'); +select to_date('19990108'); +select to_date('990108'); +select to_date('J2451187'); +set orafce.nls_date_format='YY-MonDD HH24:MI:SS'; +select to_date('14-Jan08 11:44:49+05:30'); +set orafce.nls_date_format='YY-DDMon HH24:MI:SS'; +select to_date('14-08Jan 11:44:49+05:30'); +set orafce.nls_date_format='DDMMYYYY HH24:MI:SS'; +select to_date('21052014 12:13:44+05:30'); +set orafce.nls_date_format='DDMMYY HH24:MI:SS'; +select to_date('210514 12:13:44+05:30'); +set orafce.nls_date_format='DDMMYY HH24:MI:SS.MS'; +select pg_catalog.to_date('210514 12:13:44.55'); +select oracle.to_date('210514 12:13:44.55'); + +-- Tests for oracle.to_date(text,text) +SET search_path TO oracle,"$user", public, pg_catalog; +select to_date('2014/04/25 10:13', 'YYYY/MM/DD HH:MI'); +select to_date('16-Feb-09 10:11:11', 'DD-Mon-YY HH:MI:SS'); +select to_date('02/16/09 04:12:12', 'MM/DD/YY HH24:MI:SS'); +select to_date('021609 111213', 'MMDDYY HHMISS'); +select to_date('16-Feb-09 11:12:12', 'DD-Mon-YY HH:MI:SS'); +select to_date('Feb/16/09 11:21:23', 'Mon/DD/YY HH:MI:SS'); +select to_date('February.16.2009 10:11:12', 'Month.DD.YYYY HH:MI:SS'); +select to_date('20020315111212', 'yyyymmddhh12miss'); +select to_date('January 15, 1989, 11:00 A.M.','Month dd, YYYY, HH:MI A.M.'); +select to_date('14-Jan08 11:44:49+05:30' ,'YY-MonDD HH24:MI:SS'); +select to_date('14-08Jan 11:44:49+05:30','YY-DDMon HH24:MI:SS'); +select to_date('21052014 12:13:44+05:30','DDMMYYYY HH24:MI:SS'); +select to_date('210514 12:13:44+05:30','DDMMYY HH24:MI:SS'); +SET search_path TO default; + +-- Tests for + operator with DATE and number(smallint,integer,bigint,numeric) +SET search_path TO oracle,"$user", public, pg_catalog; +SET orafce.nls_date_format='YYYY-MM-DD HH24:MI:SS'; +SELECT to_date('2014-07-02 10:08:55') + 9::smallint; +SET orafce.nls_date_format='MM-DD-YYYY HH24:MI:SS'; +SELECT to_date('07-02-2014 10:08:55') + 9::smallint; +SET orafce.nls_date_format='DD-MM-YYYY HH24:MI:SS'; +SELECT to_date('02-07-2014 10:08:55') + 9::smallint; +SET orafce.nls_date_format='YYYY-MM-DD HH24:MI:SS'; +SELECT to_date('2014-07-02 10:08:55') + 9; +SELECT to_date('2014-07-02 10:08:55','YYYY-MM-DD HH:MI:SS') + 9::smallint; +SELECT to_date('02-07-2014 10:08:55','DD-MM-YYYY HH:MI:SS') + 9::smallint; +SELECT to_date('07-02-2014 10:08:55','MM-DD-YYYY HH:MI:SS') + 9::smallint; +SET orafce.nls_date_format='YYYY-MM-DD HH24:MI:SS'; +SELECT to_date('2014-07-02 10:08:55') + 9::bigint; +SET orafce.nls_date_format='MM-DD-YYYY HH24:MI:SS'; +SELECT to_date('07-02-2014 10:08:55') + 9::bigint; +SET orafce.nls_date_format='DD-MM-YYYY HH24:MI:SS'; +SELECT to_date('02-07-2014 10:08:55') + 9::bigint; +SELECT to_date('2014-07-02 10:08:55','YYYY-MM-DD HH:MI:SS') + 9::bigint; +SELECT to_date('02-07-2014 10:08:55','DD-MM-YYYY HH:MI:SS') + 9::bigint; +SELECT to_date('07-02-2014 10:08:55','MM-DD-YYYY HH:MI:SS') + 9::bigint; +SET orafce.nls_date_format='YYYY-MM-DD HH24:MI:SS'; +SELECT to_date('2014-07-02 10:08:55') + 9::integer; +SET orafce.nls_date_format='MM-DD-YYYY HH24:MI:SS'; +SELECT to_date('07-02-2014 10:08:55') + 9::integer; +SET orafce.nls_date_format='DD-MM-YYYY HH24:MI:SS'; +SELECT to_date('02-07-2014 10:08:55') + 9::integer; +SELECT to_date('2014-07-02 10:08:55','YYYY-MM-DD HH:MI:SS') + 9::integer; +SELECT to_date('02-07-2014 10:08:55','DD-MM-YYYY HH:MI:SS') + 9::integer; +SELECT to_date('07-02-2014 10:08:55','MM-DD-YYYY HH:MI:SS') + 9::integer; +SET orafce.nls_date_format='YYYY-MM-DD HH24:MI:SS'; +SELECT to_date('2014-07-02 10:08:55') + 9::numeric; +SET orafce.nls_date_format='MM-DD-YYYY HH24:MI:SS'; +SELECT to_date('07-02-2014 10:08:55') + 9::numeric; +SET orafce.nls_date_format='DD-MM-YYYY HH24:MI:SS'; +SELECT to_date('02-07-2014 10:08:55') + 9::numeric; +SELECT to_date('2014-07-02 10:08:55','YYYY-MM-DD HH:MI:SS') + 9::numeric; +SELECT to_date('02-07-2014 10:08:55','DD-MM-YYYY HH:MI:SS') + 9::numeric; +SELECT to_date('07-02-2014 10:08:55','MM-DD-YYYY HH:MI:SS') + 9::numeric; +SET orafce.nls_date_format='YYYY-MM-DD HH24:MI:SS'; +SELECT to_date('2014-01-01 00:00:00') + 1.5; +SELECT to_date('2014-01-01 00:00:00','yyyy-mm-dd hh24:mi:ss') + 1.5; +SET search_path TO default; + +-- Tests for - operator with DATE and number(smallint,integer,bigint,numeric) +SET search_path TO oracle,"$user", public, pg_catalog; +SET orafce.nls_date_format='YYYY-MM-DD HH24:MI:SS'; +SELECT to_date('2014-07-02 10:08:55') - 9::smallint; +SET orafce.nls_date_format='MM-DD-YYYY HH24:MI:SS'; +SELECT to_date('07-02-2014 10:08:55') - 9::smallint; +SET orafce.nls_date_format='DD-MM-YYYY HH24:MI:SS'; +SELECT to_date('02-07-2014 10:08:55') - 9::smallint; +SET orafce.nls_date_format='YYYY-MM-DD HH24:MI:SS'; +SELECT to_date('2014-07-02 10:08:55') - 9; +SELECT to_date('2014-07-02 10:08:55','YYYY-MM-DD HH:MI:SS') - 9::smallint; +SELECT to_date('02-07-2014 10:08:55','DD-MM-YYYY HH:MI:SS') - 9::smallint; +SELECT to_date('07-02-2014 10:08:55','MM-DD-YYYY HH:MI:SS') - 9::smallint; +SET orafce.nls_date_format='YYYY-MM-DD HH24:MI:SS'; +SELECT to_date('2014-07-02 10:08:55') - 9::bigint; +SET orafce.nls_date_format='MM-DD-YYYY HH24:MI:SS'; +SELECT to_date('07-02-2014 10:08:55') - 9::bigint; +SET orafce.nls_date_format='DD-MM-YYYY HH24:MI:SS'; +SELECT to_date('02-07-2014 10:08:55') - 9::bigint; +SELECT to_date('2014-07-02 10:08:55','YYYY-MM-DD HH:MI:SS') - 9::bigint; +SELECT to_date('02-07-2014 10:08:55','DD-MM-YYYY HH:MI:SS') - 9::bigint; +SELECT to_date('07-02-2014 10:08:55','MM-DD-YYYY HH:MI:SS') - 9::bigint; +SET orafce.nls_date_format='YYYY-MM-DD HH24:MI:SS'; +SELECT to_date('2014-07-02 10:08:55') - 9::integer; +SET orafce.nls_date_format='MM-DD-YYYY HH24:MI:SS'; +SELECT to_date('07-02-2014 10:08:55') - 9::integer; +SET orafce.nls_date_format='DD-MM-YYYY HH24:MI:SS'; +SELECT to_date('02-07-2014 10:08:55') - 9::integer; +SELECT to_date('2014-07-02 10:08:55','YYYY-MM-DD HH:MI:SS') - 9::integer; +SELECT to_date('02-07-2014 10:08:55','DD-MM-YYYY HH:MI:SS') - 9::integer; +SELECT to_date('07-02-2014 10:08:55','MM-DD-YYYY HH:MI:SS') - 9::integer; +SET orafce.nls_date_format='YYYY-MM-DD HH24:MI:SS'; +SELECT to_date('2014-07-02 10:08:55') - 9::numeric; +SET orafce.nls_date_format='MM-DD-YYYY HH24:MI:SS'; +SELECT to_date('07-02-2014 10:08:55') - 9::numeric; +SET orafce.nls_date_format='DD-MM-YYYY HH24:MI:SS'; +SELECT to_date('02-07-2014 10:08:55') - 9::numeric; +SELECT to_date('2014-07-02 10:08:55','YYYY-MM-DD HH:MI:SS') - 9::numeric; +SELECT to_date('02-07-2014 10:08:55','DD-MM-YYYY HH:MI:SS') - 9::numeric; +SELECT to_date('07-02-2014 10:08:55','MM-DD-YYYY HH:MI:SS') - 9::numeric; +SET orafce.nls_date_format='YYYY-MM-DD HH24:MI:SS'; +SELECT to_date('2014-01-01 00:00:00') - 1.5; +SELECT to_date('2014-01-01 00:00:00','yyyy-mm-dd hh24:mi:ss') - 1.5; +SET search_path TO default; + +--Tests for oracle.to_char(timestamp)-used to set the DATE output format +SET search_path TO oracle,"$user", public, pg_catalog; +SET orafce.nls_date_format to default; +select oracle.to_char(to_date('19-APR-16 21:41:48')); +set orafce.nls_date_format='YY-MonDD HH24:MI:SS'; +select oracle.to_char(to_date('14-Jan08 11:44:49+05:30')); +set orafce.nls_date_format='YY-DDMon HH24:MI:SS'; +select oracle.to_char(to_date('14-08Jan 11:44:49+05:30')); +set orafce.nls_date_format='DDMMYYYY HH24:MI:SS'; +select oracle.to_char(to_date('21052014 12:13:44+05:30')); +set orafce.nls_date_format='DDMMYY HH24:MI:SS'; +select oracle.to_char(to_date('210514 12:13:44+05:30')); +set orafce.nls_date_format='DDMMYYYY HH24:MI:SS'; +select oracle.to_char(oracle.to_date('2014/04/25 10:13', 'YYYY/MM/DD HH:MI')); +set orafce.nls_date_format='YY-DDMon HH24:MI:SS'; +select oracle.to_char(oracle.to_date('16-Feb-09 10:11:11', 'DD-Mon-YY HH:MI:SS')); +set orafce.nls_date_format='YY-DDMon HH24:MI:SS'; +select oracle.to_char(oracle.to_date('02/16/09 04:12:12', 'MM/DD/YY HH24:MI:SS')); +set orafce.nls_date_format='YY-MonDD HH24:MI:SS'; +select oracle.to_char(oracle.to_date('021609 111213', 'MMDDYY HHMISS')); +set orafce.nls_date_format='DDMMYYYY HH24:MI:SS'; +select oracle.to_char(oracle.to_date('16-Feb-09 11:12:12', 'DD-Mon-YY HH:MI:SS')); +set orafce.nls_date_format='DDMMYYYY HH24:MI:SS'; +select oracle.to_char(oracle.to_date('Feb/16/09 11:21:23', 'Mon/DD/YY HH:MI:SS')); +set orafce.nls_date_format='DDMMYYYY HH24:MI:SS'; +select oracle.to_char(oracle.to_date('February.16.2009 10:11:12', 'Month.DD.YYYY HH:MI:SS')); +set orafce.nls_date_format='YY-MonDD HH24:MI:SS'; +select oracle.to_char(oracle.to_date('20020315111212', 'yyyymmddhh12miss')); +set orafce.nls_date_format='DDMMYYYY HH24:MI:SS'; +select oracle.to_char(oracle.to_date('January 15, 1989, 11:00 A.M.','Month dd, YYYY, HH:MI A.M.')); +set orafce.nls_date_format='DDMMYY HH24:MI:SS'; +select oracle.to_char(oracle.to_date('14-Jan08 11:44:49+05:30' ,'YY-MonDD HH24:MI:SS')); +set orafce.nls_date_format='DDMMYYYY HH24:MI:SS'; +select oracle.to_char(oracle.to_date('14-08Jan 11:44:49+05:30','YY-DDMon HH24:MI:SS')); +set orafce.nls_date_format='YY-MonDD HH24:MI:SS'; +select oracle.to_char(oracle.to_date('21052014 12:13:44+05:30','DDMMYYYY HH24:MI:SS')); +set orafce.nls_date_format='DDMMYY HH24:MI:SS'; +select oracle.to_char(oracle.to_date('210514 12:13:44+05:30','DDMMYY HH24:MI:SS')); +SET search_path TO default; + +--Tests for oracle.-(oracle.date,oracle.date) +SET search_path TO oracle,"$user", public, pg_catalog; +SELECT (to_date('2014-07-17 11:10:15', 'yyyy-mm-dd hh24:mi:ss') - to_date('2014-02-01 10:00:00', 'yyyy-mm-dd hh24:mi:ss'))::numeric(10,4); +SELECT (to_date('2014-07-17 13:14:15', 'yyyy-mm-dd hh24:mi:ss') - to_date('2014-02-01 10:00:00', 'yyyy-mm-dd hh24:mi:ss'))::numeric(10,4); +SELECT (to_date('07-17-2014 13:14:15', 'mm-dd-yyyy hh24:mi:ss') - to_date('2014-02-01 10:00:00', 'yyyy-mm-dd hh24:mi:ss'))::numeric(10,4); +SELECT (to_date('07-17-2014 13:14:15', 'mm-dd-yyyy hh24:mi:ss') - to_date('2015-02-01 10:00:00', 'yyyy-mm-dd hh24:mi:ss'))::numeric(10,4); +SELECT (to_date('07-17-2014 13:14:15', 'mm-dd-yyyy hh24:mi:ss') - to_date('01-01-2013 10:00:00', 'mm-dd-yyyy hh24:mi:ss'))::numeric(10,4); +SELECT (to_date('17-07-2014 13:14:15', 'dd-mm-yyyy hh24:mi:ss') - to_date('01-01-2013 10:00:00', 'dd--mm-yyyy hh24:mi:ss'))::numeric(10,4); +SELECT (to_date('2014/02/01 10:11:12', 'YYYY/MM/DD hh12:mi:ss') - to_date('2013/02/01 10:11:12', 'YYYY/MM/DD hh12:mi:ss'))::numeric(10,4); +SELECT (to_date('17-Jul-14 10:11:11', 'DD-Mon-YY HH:MI:SS') - to_date('17-Jan-14 00:00:00', 'DD-Mon-YY HH24:MI:SS'))::numeric(10,4); +SELECT (to_date('July.17.2014 10:11:12', 'Month.DD.YYYY HH:MI:SS') - to_date('February.16.2014 10:21:12', 'Month.DD.YYYY HH:MI:SS'))::numeric(10,4); +SELECT (to_date('20140717111211', 'yyyymmddhh12miss') - to_date('20140315111212', 'yyyymmddhh12miss'))::numeric(10,4); +SELECT (to_date('January 15, 1990, 11:00 A.M.','Month dd, YYYY, HH:MI A.M.') - to_date('January 15, 1989, 10:00 A.M.','Month dd, YYYY, HH:MI A.M.'))::numeric(10,4); +SELECT (to_date('14-Jul14 11:44:49' ,'YY-MonDD HH24:MI:SS') - to_date('14-Jan14 12:44:49' ,'YY-MonDD HH24:MI:SS'))::numeric(10,4); +SELECT (to_date('210514 12:13:44','DDMMYY HH24:MI:SS') - to_date('210114 10:13:44','DDMMYY HH24:MI:SS'))::numeric(10,4); +SELECT trunc(to_date('210514 12:13:44','DDMMYY HH24:MI:SS')); +SELECT round(to_date('210514 12:13:44','DDMMYY HH24:MI:SS')); + + +SET search_path TO default; + +-- +-- Note: each Japanese character used below has display width of 2, otherwise 1. +-- Note: each output string is surrounded by '|' for improved readability +-- + +-- +-- test LPAD family of functions +-- + +/* cases where one or more arguments are of type CHAR */ +SELECT '|' || oracle.lpad('あbcd'::char(8), 10) || '|'; +SELECT '|' || oracle.lpad('あbcd'::char(8), 5) || '|'; +SELECT '|' || oracle.lpad('あbcd'::char(8), 1) || '|'; + +SELECT '|' || oracle.lpad('あbcd'::char(5), 10, 'xい'::char(3)) || '|'; +SELECT '|' || oracle.lpad('あbcd'::char(5), 5, 'xい'::char(3)) || '|'; + +SELECT '|' || oracle.lpad('あbcd'::char(5), 10, 'xい'::text) || '|'; +SELECT '|' || oracle.lpad('あbcd'::char(5), 10, 'xい'::varchar2(5)) || '|'; +SELECT '|' || oracle.lpad('あbcd'::char(5), 10, 'xい'::nvarchar2(3)) || '|'; + +SELECT '|' || oracle.lpad('あbcd'::text, 10, 'xい'::char(3)) || '|'; +SELECT '|' || oracle.lpad('あbcd'::text, 5, 'xい'::char(3)) || '|'; + +SELECT '|' || oracle.lpad('あbcd'::varchar2(5), 10, 'xい'::char(3)) || '|'; +SELECT '|' || oracle.lpad('あbcd'::varchar2(5), 5, 'xい'::char(3)) || '|'; + +SELECT '|' || oracle.lpad('あbcd'::nvarchar2(5), 10, 'xい'::char(3)) || '|'; +SELECT '|' || oracle.lpad('あbcd'::nvarchar2(5), 5, 'xい'::char(3)) || '|'; + +/* test oracle.lpad(text, int [, text]) */ +SELECT '|' || oracle.lpad('あbcd'::text, 10) || '|'; +SELECT '|' || oracle.lpad('あbcd'::text, 5) || '|'; + +SELECT '|' || oracle.lpad('あbcd'::varchar2(10), 10) || '|'; +SELECT '|' || oracle.lpad('あbcd'::varchar2(10), 5) || '|'; + +SELECT '|' || oracle.lpad('あbcd'::nvarchar2(10), 10) || '|'; +SELECT '|' || oracle.lpad('あbcd'::nvarchar2(10), 5) || '|'; + +SELECT '|' || oracle.lpad('あbcd'::text, 10, 'xい'::text) || '|'; +SELECT '|' || oracle.lpad('あbcd'::text, 10, 'xい'::varchar2(5)) || '|'; +SELECT '|' || oracle.lpad('あbcd'::text, 10, 'xい'::nvarchar2(3)) || '|'; + +SELECT '|' || oracle.lpad('あbcd'::varchar2(5), 10, 'xい'::text) || '|'; +SELECT '|' || oracle.lpad('あbcd'::varchar2(5), 10, 'xい'::varchar2(5)) || '|'; +SELECT '|' || oracle.lpad('あbcd'::varchar2(5), 10, 'xい'::nvarchar2(5)) || '|'; + +SELECT '|' || oracle.lpad('あbcd'::nvarchar2(5), 10, 'xい'::text) || '|'; +SELECT '|' || oracle.lpad('あbcd'::nvarchar2(5), 10, 'xい'::varchar2(5)) || '|'; +SELECT '|' || oracle.lpad('あbcd'::nvarchar2(5), 10, 'xい'::nvarchar2(5)) || '|'; + +-- +-- test RPAD family of functions +-- + +/* cases where one or more arguments are of type CHAR */ +SELECT '|' || oracle.rpad('あbcd'::char(8), 10) || '|'; +SELECT '|' || oracle.rpad('あbcd'::char(8), 5) || '|'; +SELECT '|' || oracle.rpad('あbcd'::char(8), 1) || '|'; + +SELECT '|' || oracle.rpad('あbcd'::char(5), 10, 'xい'::char(3)) || '|'; +SELECT '|' || oracle.rpad('あbcd'::char(5), 5, 'xい'::char(3)) || '|'; + +SELECT '|' || oracle.rpad('あbcd'::char(5), 10, 'xい'::text) || '|'; +SELECT '|' || oracle.rpad('あbcd'::char(5), 10, 'xい'::varchar2(5)) || '|'; +SELECT '|' || oracle.rpad('あbcd'::char(5), 10, 'xい'::nvarchar2(3)) || '|'; + +SELECT '|' || oracle.rpad('あbcd'::text, 10, 'xい'::char(3)) || '|'; +SELECT '|' || oracle.rpad('あbcd'::text, 5, 'xい'::char(3)) || '|'; + +SELECT '|' || oracle.rpad('あbcd'::varchar2(5), 10, 'xい'::char(3)) || '|'; +SELECT '|' || oracle.rpad('あbcd'::varchar2(5), 5, 'xい'::char(3)) || '|'; + +SELECT '|' || oracle.rpad('あbcd'::nvarchar2(5), 10, 'xい'::char(3)) || '|'; +SELECT '|' || oracle.rpad('あbcd'::nvarchar2(5), 5, 'xい'::char(3)) || '|'; + +/* test oracle.lpad(text, int [, text]) */ +SELECT '|' || oracle.rpad('あbcd'::text, 10) || '|'; +SELECT '|' || oracle.rpad('あbcd'::text, 5) || '|'; + +SELECT '|' || oracle.rpad('あbcd'::varchar2(10), 10) || '|'; +SELECT '|' || oracle.rpad('あbcd'::varchar2(10), 5) || '|'; + +SELECT '|' || oracle.rpad('あbcd'::nvarchar2(10), 10) || '|'; +SELECT '|' || oracle.rpad('あbcd'::nvarchar2(10), 5) || '|'; + +SELECT '|' || oracle.rpad('あbcd'::text, 10, 'xい'::text) || '|'; +SELECT '|' || oracle.rpad('あbcd'::text, 10, 'xい'::varchar2(5)) || '|'; +SELECT '|' || oracle.rpad('あbcd'::text, 10, 'xい'::nvarchar2(3)) || '|'; + +SELECT '|' || oracle.rpad('あbcd'::varchar2(5), 10, 'xい'::text) || '|'; +SELECT '|' || oracle.rpad('あbcd'::varchar2(5), 10, 'xい'::varchar2(5)) || '|'; +SELECT '|' || oracle.rpad('あbcd'::varchar2(5), 10, 'xい'::nvarchar2(5)) || '|'; + +SELECT '|' || oracle.rpad('あbcd'::nvarchar2(5), 10, 'xい'::text) || '|'; +SELECT '|' || oracle.rpad('あbcd'::nvarchar2(5), 10, 'xい'::varchar2(5)) || '|'; +SELECT '|' || oracle.rpad('あbcd'::nvarchar2(5), 10, 'xい'::nvarchar2(5)) || '|'; + +-- +-- test TRIM family of functions +-- + +/* test that trailing blanks of CHAR arguments are not removed and are significant */ + +-- +-- LTRIM +-- +SELECT '|' || oracle.ltrim(' abcd'::char(10)) || '|' as LTRIM; +SELECT '|' || oracle.ltrim(' abcd'::char(10),'a'::char(3)) || '|' as LTRIM; +SELECT '|' || oracle.ltrim(' abcd'::char(10),'a'::text) || '|' as LTRIM; +SELECT '|' || oracle.ltrim(' abcd'::char(10),'a'::varchar2(3)) || '|' as LTRIM; +SELECT '|' || oracle.ltrim(' abcd'::char(10),'a'::nvarchar2(3)) || '|' as LTRIM; + +SELECT '|' || oracle.ltrim(' abcd '::text,'a'::char(3)) || '|' as LTRIM; +SELECT '|' || oracle.ltrim(' abcd '::varchar2(10),'a'::char(3)) || '|' as LTRIM; +SELECT '|' || oracle.ltrim(' abcd '::nvarchar2(10),'a'::char(3)) || '|' as LTRIM; + +-- +-- RTRIM +-- +SELECT '|' || oracle.rtrim(' abcd'::char(10)) || '|' as LTRIM; +SELECT '|' || oracle.rtrim(' abcd'::char(10),'d'::char(3)) || '|' as LTRIM; +SELECT '|' || oracle.rtrim(' abcd'::char(10),'d'::text) || '|' as LTRIM; +SELECT '|' || oracle.rtrim(' abcd'::char(10),'d'::varchar2(3)) || '|' as LTRIM; +SELECT '|' || oracle.rtrim(' abcd'::char(10),'d'::nvarchar2(3)) || '|' as LTRIM; + +SELECT '|' || oracle.rtrim(' abcd '::text,'d'::char(3)) || '|' as LTRIM; +SELECT '|' || oracle.rtrim(' abcd '::varchar2(10),'d'::char(3)) || '|' as LTRIM; +SELECT '|' || oracle.rtrim(' abcd '::nvarchar2(10),'d'::char(3)) || '|' as LTRIM; + +-- +-- BTRIM +-- +SELECT '|' || oracle.btrim(' abcd'::char(10)) || '|' as LTRIM; +SELECT '|' || oracle.btrim(' abcd'::char(10),'ad'::char(3)) || '|' as LTRIM; +SELECT '|' || oracle.btrim(' abcd'::char(10),'ad'::text) || '|' as LTRIM; +SELECT '|' || oracle.btrim(' abcd'::char(10),'ad'::varchar2(3)) || '|' as LTRIM; +SELECT '|' || oracle.btrim(' abcd'::char(10),'ad'::nvarchar2(3)) || '|' as LTRIM; + +SELECT '|' || oracle.btrim(' abcd '::text,'d'::char(3)) || '|' as LTRIM; +SELECT '|' || oracle.btrim(' abcd '::varchar2(10),'d'::char(3)) || '|' as LTRIM; +SELECT '|' || oracle.btrim(' abcd '::nvarchar2(10),'d'::char(3)) || '|' as LTRIM; + +-- +-- test oracle.length() +-- + +/* test that trailing blanks are not ignored */ +SELECT oracle.length('あbb'::char(6)); +SELECT oracle.length(''::char(6)); + + +-- +-- test plvdate.bizdays_between +-- +SELECT plvdate.including_start(); +SELECT plvdate.bizdays_between('2016-02-24','2016-02-26'); +SELECT plvdate.bizdays_between('2016-02-21','2016-02-27'); +SELECT plvdate.include_start(false); +SELECT plvdate.bizdays_between('2016-02-24','2016-02-26'); +SELECT plvdate.bizdays_between('2016-02-21','2016-02-27'); + +SELECT oracle.round(1.234::double precision, 2), oracle.trunc(1.234::double precision, 2); +SELECT oracle.round(1.234::float, 2), oracle.trunc(1.234::float, 2); + +-- +-- should not fail - fix: Crashes due to insufficent argument checking (#59) +-- +select dbms_random.string(null, 42); +select dbms_pipe.create_pipe(null); +select plunit.assert_not_equals(1,2,3); + +-- +-- lexer text +-- +SELECT pos, token, class, mod FROM plvlex.tokens('select * from a.b.c join d on x=y', true, true); + +-- +-- trigger functions +-- + +CREATE TABLE trg_test(a varchar, b int, c varchar, d date, e int); + +CREATE TRIGGER trg_test_xx BEFORE INSERT OR UPDATE + ON trg_test FOR EACH ROW EXECUTE PROCEDURE oracle.replace_empty_strings(true); + +\pset null *** + +INSERT INTO trg_test VALUES('',10, 'AHOJ', NULL, NULL); +INSERT INTO trg_test VALUES('AHOJ', NULL, '', '2020-01-01', 100); +SELECT * FROM trg_test order by a asc; + +DELETE FROM trg_test; + +DROP TRIGGER trg_test_xx ON trg_test; + +CREATE TRIGGER trg_test_xx BEFORE INSERT OR UPDATE + ON trg_test FOR EACH ROW EXECUTE PROCEDURE oracle.replace_null_strings(); + +INSERT INTO trg_test VALUES(NULL, 10, 'AHOJ', NULL, NULL); +INSERT INTO trg_test VALUES('AHOJ', NULL, NULL, '2020-01-01', 100); + +SELECT * FROM trg_test order by a asc; + +DROP TABLE trg_test; + +SELECT oracle.unistr('\0441\043B\043E\043D'); +SELECT oracle.unistr('d\u0061t\U00000061'); + +-- run-time error +SELECT oracle.unistr('wrong: \db99'); +SELECT oracle.unistr('wrong: \db99\0061'); +SELECT oracle.unistr('wrong: \+00db99\+000061'); +SELECT oracle.unistr('wrong: \+2FFFFF'); +SELECT oracle.unistr('wrong: \udb99\u0061'); +SELECT oracle.unistr('wrong: \U0000db99\U00000061'); +SELECT oracle.unistr('wrong: \U002FFFFF'); diff --git a/src/postgres/src/test/regress/sql/yb_pg_orafce2.sql b/src/postgres/src/test/regress/sql/yb_pg_orafce2.sql new file mode 100644 index 000000000000..5f5bde943210 --- /dev/null +++ b/src/postgres/src/test/regress/sql/yb_pg_orafce2.sql @@ -0,0 +1,3 @@ +-- 2) fails and throws error: 'ERROR: could not determine polymorphic type +-- because input has type "unknown"' +select decode('2012-01-01', '2012-01-01', 23, '2012-01-02', 24); diff --git a/src/postgres/src/test/regress/sql/yb_pg_orafce_aggregates.sql b/src/postgres/src/test/regress/sql/yb_pg_orafce_aggregates.sql new file mode 100644 index 000000000000..3b8483876cf0 --- /dev/null +++ b/src/postgres/src/test/regress/sql/yb_pg_orafce_aggregates.sql @@ -0,0 +1,89 @@ +-- Tests for the aggregate listagg +SELECT listagg(i::text) from generate_series(1,3) g(i); +SELECT listagg(i::text, ',') from generate_series(1,3) g(i); +SELECT coalesce(listagg(i::text), '') from (SELECT ''::text) g(i); +SELECT coalesce(listagg(i::text), '') from generate_series(1,0) g(i); +SELECT wm_concat(i::text) from generate_series(1,3) g(i); + +-- Tests for the aggregate median( real | double ) +CREATE FUNCTION checkMedianRealOdd() RETURNS real AS $$ +DECLARE + med real; + +BEGIN + CREATE TABLE median_test (salary real); + INSERT INTO median_test VALUES (4500); + INSERT INTO median_test VALUES (NULL); + INSERT INTO median_test VALUES (2100); + INSERT INTO median_test VALUES (3600); + INSERT INTO median_test VALUES (4000); + SELECT into med median(salary) from median_test; + DROP TABLE median_test; + return med; + +END; +$$ LANGUAGE plpgsql; + +CREATE FUNCTION checkMedianRealEven() RETURNS real AS $$ +DECLARE + med real; + +BEGIN + CREATE TABLE median_test (salary real); + INSERT INTO median_test VALUES (4500); + INSERT INTO median_test VALUES (1500); + INSERT INTO median_test VALUES (2100); + INSERT INTO median_test VALUES (3600); + INSERT INTO median_test VALUES (1000); + INSERT INTO median_test VALUES (4000); + select into med median(salary) from median_test; + DROP TABLE median_test; + return med; +END; +$$ LANGUAGE plpgsql; + +CREATE FUNCTION checkMedianDoubleOdd() RETURNS double precision AS $$ +DECLARE + med double precision; +BEGIN + CREATE TABLE median_test (salary double precision); + INSERT INTO median_test VALUES (4500); + INSERT INTO median_test VALUES (1500); + INSERT INTO median_test VALUES (2100); + INSERT INTO median_test VALUES (3600); + INSERT INTO median_test VALUES (4000); + select into med median(salary) from median_test; + DROP TABLE median_test; + return med; +END; +$$ LANGUAGE plpgsql; + +CREATE FUNCTION checkMedianDoubleEven() RETURNS double precision AS $$ +DECLARE + med double precision; + +BEGIN + CREATE TABLE median_test (salary double precision); + INSERT INTO median_test VALUES (4500); + INSERT INTO median_test VALUES (1500); + INSERT INTO median_test VALUES (2100); + INSERT INTO median_test VALUES (3600); + INSERT INTO median_test VALUES (4000); + INSERT INTO median_test VALUES (1000); + select into med median(salary) from median_test; + DROP TABLE median_test; + return med; +END; +$$ LANGUAGE plpgsql; + +SELECT checkMedianRealOdd(); +SELECT checkMedianRealEven(); +SELECT checkMedianDoubleOdd(); +SELECT checkMedianDoubleEven(); + +DROP FUNCTION checkMedianRealOdd(); +DROP FUNCTION checkMedianRealEven(); +DROP FUNCTION checkMedianDoubleOdd(); +DROP FUNCTION checkMedianDoubleEven(); + + diff --git a/src/postgres/src/test/regress/sql/yb_pg_orafce_dbms_alert_session_A.sql b/src/postgres/src/test/regress/sql/yb_pg_orafce_dbms_alert_session_A.sql new file mode 100644 index 000000000000..18cbb6b60707 --- /dev/null +++ b/src/postgres/src/test/regress/sql/yb_pg_orafce_dbms_alert_session_A.sql @@ -0,0 +1,66 @@ +\set ECHO all + +SELECT pg_sleep(3); + +/* + * DBMS_ALERT is used for one-way communication of one session to other. + * + * This session mainly sends signals for testing the alert functionality in + * session B and C. + * + * The following alerts are used to ensure that signals are sent at correct + * times to session B for testing. These signals are sent from session B + * indicating completion of an event. + * After the signal is received, the next required signal for testing is sent + * from this session. + */ + +SELECT dbms_alert.register('b1'); +SELECT dbms_alert.register('b2'); +SELECT dbms_alert.register('b3'); +SELECT dbms_alert.register('b4'); +SELECT dbms_alert.register('b5'); + +SELECT dbms_alert.signal('a1','Msg1 for a1'); + +SELECT dbms_alert.signal('a2','Msg1 for a2'); + +/* + * Test: defered_signal + * The signal is received only when the signalling transaction commits. + * To test this, an explict BEGIN-COMMIT block is used. + */ +SELECT dbms_alert.signal('tds','Begin defered_signal test'); +BEGIN; +SELECT dbms_alert.signal('tds','Testing defered_signal'); +/* The signal is received while transaction is running */ +SELECT dbms_alert.waitone('b1',20); +COMMIT; +/* The signal is received after transaction completed. + * After this the tds signal is received in session B indicating that the + * signal is received only after commit. + */ +SELECT dbms_alert.waitone('b1',20); + +SELECT dbms_alert.waitone('b2',20); +/* This signals a3 which is not registered in Session B */ +SELECT dbms_alert.signal('a3','Msg1 for a3'); +/* alert a4 is signalled soon after a3 */ +SELECT dbms_alert.signal('a4','Test- Register after signal'); + +/* This signal indicates at remove() is called */ +SELECT dbms_alert.waitone('b3',20); +/* Send signal which is removed in session B */ +SELECT dbms_alert.signal('a1','Msg2 for a1'); + +SELECT dbms_alert.waitone('b4',20); +/* Send signal which is registered in B and not removed */ +SELECT dbms_alert.signal('a4','Msg1 for a4'); + +/* This signal inidcates that removeall() is called */ +SELECT dbms_alert.waitone('b5',20); +/* Send a signal to test if session B receives it after removeall() */ +SELECT dbms_alert.signal('a2','Msg2 for a2'); + +/* cleanup */ +SELECT dbms_alert.removeall(); diff --git a/src/postgres/src/test/regress/sql/yb_pg_orafce_dbms_alert_session_B.sql b/src/postgres/src/test/regress/sql/yb_pg_orafce_dbms_alert_session_B.sql new file mode 100644 index 000000000000..f7f3008306d6 --- /dev/null +++ b/src/postgres/src/test/regress/sql/yb_pg_orafce_dbms_alert_session_B.sql @@ -0,0 +1,49 @@ +\set ECHO all + +/* Register alerts */ +SELECT dbms_alert.register('a1'); +SELECT dbms_alert.register('a2'); +SELECT dbms_alert.register('tds'); + +/* Test: multisession waitone */ +SELECT dbms_alert.waitone('a1',20); + +/* Test: multisession waitany */ +SELECT dbms_alert.waitany(10); + +/* Test defered_signal */ +/* This indicated that the transaction has begun */ +SELECT dbms_alert.waitone('tds',10); +/* The signal will not be received because the transaction is running */ +SELECT dbms_alert.waitone('tds',2); +SELECT dbms_alert.signal('b1','Transaction still running'); +SELECT dbms_alert.signal('b1','Transaction committed'); +/* Since the transaction has commited, the signal will be received */ +SELECT dbms_alert.waitone('tds',10); + +/* Signal session A to send msg1 for a3 */ +SELECT dbms_alert.signal('b2','to check unregistered alert wait'); +/* Test: wait for unregistered alert which is signaled*/ +SELECT dbms_alert.waitone('a3',2); + +/* Test: Register after alert is signaled and wait */ +SELECT dbms_alert.register('a4'); +SELECT dbms_alert.waitone('a4',2); + +/* Test: remove one */ +SELECT dbms_alert.remove('a1'); +/* Signal session A to send msg2 for a1 */ +SELECT dbms_alert.signal('b3','remove(a1) called'); +/* Test: wait for removed alert */ +SELECT dbms_alert.waitone('a1',2); +/* Signal session A to send msg1 for a4 */ +SELECT dbms_alert.signal('b4','to check unremoved alert'); +/* Test: Check if unremoved alert is received */ +SELECT dbms_alert.waitone('a4',10); + +/* Test removeall */ +SELECT dbms_alert.removeall(); +/* Signal session A to send msg2 for a2 */ +SELECT dbms_alert.signal('b5','removeall called'); +/* Test: Use waitany to see if any alert is received */ +SELECT dbms_alert.waitany(2); diff --git a/src/postgres/src/test/regress/sql/yb_pg_orafce_dbms_alert_session_C.sql b/src/postgres/src/test/regress/sql/yb_pg_orafce_dbms_alert_session_C.sql new file mode 100644 index 000000000000..17b327b6bbd2 --- /dev/null +++ b/src/postgres/src/test/regress/sql/yb_pg_orafce_dbms_alert_session_C.sql @@ -0,0 +1,15 @@ +\set ECHO all + +/* Register alerts */ +SELECT dbms_alert.register('a1'); +SELECT dbms_alert.register('a2'); + +/* Test: multisession waitone */ +SELECT dbms_alert.waitone('a1',20); + +/* Test: multisession waitany */ +SELECT dbms_alert.waitany(10); + +/* cleanup */ +SELECT dbms_alert.removeall(); + diff --git a/src/postgres/src/test/regress/sql/yb_pg_orafce_dbms_output.sql b/src/postgres/src/test/regress/sql/yb_pg_orafce_dbms_output.sql new file mode 100644 index 000000000000..cc0229e23dea --- /dev/null +++ b/src/postgres/src/test/regress/sql/yb_pg_orafce_dbms_output.sql @@ -0,0 +1,764 @@ +\set ECHO none +SET client_min_messages = warning; +SET DATESTYLE TO ISO; +SET client_encoding = utf8; +\pset null '' +\set ECHO all + +DROP FUNCTION dbms_output_test(); +DROP TABLE dbms_output_test; + +-- DBMS_OUTPUT.DISABLE [0] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER; +BEGIN + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +SELECT * FROM dbms_output_test order by buff asc; +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); + +-- DBMS_OUTPUT.PUT_LINE [1] +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff1 VARCHAR(20) := 'orafce'; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('t'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE'); + PERFORM DBMS_OUTPUT.PUT_LINE (buff1); + PERFORM DBMS_OUTPUT.PUT ('ABC'); + PERFORM DBMS_OUTPUT.PUT_LINE (''); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +DROP FUNCTION dbms_output_test(); + +-- DBMS_OUTPUT.PUT_LINE [2] +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('t'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORA +F +CE'); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +DROP FUNCTION dbms_output_test(); + +-- DBMS_OUTPUT.PUT [1] +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff1 VARCHAR(20) := 'ora'; + buff2 VARCHAR(20) := 'f'; + buff3 VARCHAR(20) := 'ce'; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('t'); + PERFORM DBMS_OUTPUT.PUT ('ORA'); + PERFORM DBMS_OUTPUT.PUT ('F'); + PERFORM DBMS_OUTPUT.PUT ('CE'); + PERFORM DBMS_OUTPUT.PUT_LINE (''); + PERFORM DBMS_OUTPUT.PUT ('ABC'); + PERFORM DBMS_OUTPUT.PUT_LINE (''); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +DROP FUNCTION dbms_output_test(); + +-- DBMS_OUTPUT.PUT [2] +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('t'); + PERFORM DBMS_OUTPUT.PUT ('ORA +F +CE'); + PERFORM DBMS_OUTPUT.PUT_LINE (''); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +DROP FUNCTION dbms_output_test(); + +-- DBMS_OUTPUT.GET_LINE [1] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 2'); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +SELECT * FROM dbms_output_test order by buff asc; +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); + +-- DBMS_OUTPUT.GET_LINE [2] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 2'); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 3'); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +SELECT * FROM dbms_output_test order by buff asc; +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); + +-- DBMS_OUTPUT.GET_LINE [3] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 2'); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.PUT ('ORA'); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +SELECT * FROM dbms_output_test order by buff asc; +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); + +-- DBMS_OUTPUT.GET_LINE [4] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 2'); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.NEW_LINE(); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +SELECT * FROM dbms_output_test order by buff asc; +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); + +-- DBMS_OUTPUT.GET_LINE [5] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1 +'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 2'); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +SELECT REPLACE(buff, ' +', '') FROM dbms_output_test order by buff asc; +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); + +-- DBMS_OUTPUT.GET_LINE [6] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORA +F +CE'); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +SELECT regexp_replace(buff, E'\n', '', 'g') FROM dbms_output_test limit 1; +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); + +-- DBMS_OUTPUT.GET_LINES [1] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + buff1 VARCHAR(20); + buff2 VARCHAR(20); + buff3 VARCHAR(20); + stts INTEGER := 10; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 2'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 3'); + SELECT INTO buff1,buff2,buff3,stts lines[1],lines[2],lines[3],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff1, stts); + INSERT INTO dbms_output_test VALUES (buff2, stts); + INSERT INTO dbms_output_test VALUES (buff3, stts); + SELECT INTO buff,stts lines[1],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +SELECT * FROM dbms_output_test order by buff asc; +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); + +-- DBMS_OUTPUT.GET_LINES [2] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + buff1 VARCHAR(20); + buff2 VARCHAR(20); + stts INTEGER := 2; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 2'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 3'); + SELECT INTO buff1,buff2,stts lines[1],lines[2],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff1, stts); + INSERT INTO dbms_output_test VALUES (buff2, stts); + SELECT INTO buff,stts lines[1],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +SELECT * FROM dbms_output_test order by buff asc; +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); + +-- DBMS_OUTPUT.GET_LINES [3] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER := 1; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 2'); + SELECT INTO buff,stts lines[1],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 3'); + SELECT INTO buff,stts lines[1],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff, stts); + SELECT INTO buff,stts lines[1],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +SELECT * FROM dbms_output_test order by buff asc; +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); + +-- DBMS_OUTPUT.GET_LINES [4] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER := 1; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 2'); + SELECT INTO buff,stts lines[1],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.PUT ('ORA'); + SELECT INTO buff,stts lines[1],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff, stts); + SELECT INTO buff,stts lines[1],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +SELECT * FROM dbms_output_test order by buff asc; +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); + +-- DBMS_OUTPUT.GET_LINES [5] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER := 1; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 2'); + SELECT INTO buff,stts lines[1],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.NEW_LINE(); + SELECT INTO buff,stts lines[1],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff, stts); + SELECT INTO buff,stts lines[1],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +SELECT * FROM dbms_output_test order by buff asc; +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); + +-- DBMS_OUTPUT.GET_LINES [6] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER := 1; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORA +F +CE'); + SELECT INTO buff,stts lines[1],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +SELECT regexp_replace(buff, E'\n', '', 'g') FROM dbms_output_test limit 1; +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); + +-- DBMS_OUTPUT.NEW_LINE [1] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff1 VARCHAR(20); + buff2 VARCHAR(20); + stts INTEGER := 10; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT ('ORA'); + PERFORM DBMS_OUTPUT.NEW_LINE(); + PERFORM DBMS_OUTPUT.PUT ('FCE'); + PERFORM DBMS_OUTPUT.NEW_LINE(); + SELECT INTO buff1,buff2,stts lines[1],lines[2],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff1, stts); + INSERT INTO dbms_output_test VALUES (buff2, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +SELECT * FROM dbms_output_test order by buff asc; +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); + +-- DBMS_OUTPUT.NEW_LINE [2] +CREATE TABLE dbms_output_test (buff VARCHAR(3000), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff1 VARCHAR(3000); + stts INTEGER := 10; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.ENABLE(2000); + FOR j IN 1..1999 LOOP + PERFORM DBMS_OUTPUT.PUT ('A'); + END LOOP; + PERFORM DBMS_OUTPUT.NEW_LINE(); + SELECT INTO buff1,stts lines[1],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff1, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +SELECT buff FROM dbms_output_test order by buff asc; +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); + +-- DBMS_OUTPUT.DISABLE [1] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 2'); + PERFORM DBMS_OUTPUT.ENABLE(); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 3'); + PERFORM DBMS_OUTPUT.DISABLE(); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.ENABLE(); + + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.PUT ('ORAFCE TEST 4'); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.NEW_LINE(); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.PUT ('ORAFCE TEST 5'); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.NEW_LINE(); + PERFORM DBMS_OUTPUT.ENABLE(); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +SELECT * FROM dbms_output_test order by buff asc; +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); + +-- DBMS_OUTPUT.DISABLE [2] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER := 10; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + SELECT INTO buff,stts lines[1],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +SELECT * FROM dbms_output_test order by buff asc; +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); + +-- DBMS_OUTPUT.ENABLE [1] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + status INTEGER; + num INTEGER := 2000; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('t'); + PERFORM DBMS_OUTPUT.ENABLE(2000); + PERFORM DBMS_OUTPUT.PUT ('ORAFCE TEST 1'); + PERFORM DBMS_OUTPUT.NEW_LINE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +SELECT * FROM dbms_output_test order by buff asc; +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); + +-- DBMS_OUTPUT.ENABLE [2] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.PUT ('ORAFCE TEST 2'); + PERFORM DBMS_OUTPUT.NEW_LINE(); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +SELECT * FROM dbms_output_test order by buff asc; +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); + +-- DBMS_OUTPUT.ENABLE [3] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER := 10; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + SELECT INTO buff,stts lines[1],numlines FROM DBMS_OUTPUT.GET_LINES(stts); + INSERT INTO dbms_output_test VALUES (buff, stts); + +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +SELECT * FROM dbms_output_test order by buff asc; +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); + +-- DBMS_OUTPUT.ENABLE [4] +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('t'); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + FOR j IN 1..2000 LOOP + PERFORM DBMS_OUTPUT.PUT ('A'); + END LOOP; + PERFORM DBMS_OUTPUT.NEW_LINE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +DROP FUNCTION dbms_output_test(); + +-- DBMS_OUTPUT.ENABLE [5] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(NULL); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +SELECT * FROM dbms_output_test order by buff asc; +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); + +-- DBMS_OUTPUT.ENABLE [6] +CREATE TABLE dbms_output_test (buff VARCHAR(20), status INTEGER); +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +DECLARE + buff VARCHAR(20); + stts INTEGER; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); + PERFORM DBMS_OUTPUT.ENABLE(); + SELECT INTO buff,stts line,status FROM DBMS_OUTPUT.GET_LINE(); + INSERT INTO dbms_output_test VALUES (buff, stts); + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +SELECT * FROM dbms_output_test order by buff asc; +DROP TABLE dbms_output_test; +DROP FUNCTION dbms_output_test(); + +-- SERVEROUTPUT [1] +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +DROP FUNCTION dbms_output_test(); + +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +BEGIn + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('t'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 2'); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +DROP FUNCTION dbms_output_test(); + +-- SERVEROUTPUT [2] +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.PUT ('ORAFCE TEST 1'); + PERFORM DBMS_OUTPUT.NEW_LINE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +DROP FUNCTION dbms_output_test(); + +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('t'); + PERFORM DBMS_OUTPUT.PUT ('ORAFCE TEST 2'); + PERFORM DBMS_OUTPUT.NEW_LINE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +DROP FUNCTION dbms_output_test(); + +-- SERVEROUTPUT [3] +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('f'); + PERFORM DBMS_OUTPUT.DISABLE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +DROP FUNCTION dbms_output_test(); + +CREATE FUNCTION dbms_output_test() RETURNS VOID AS $$ +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT ('t'); + PERFORM DBMS_OUTPUT.PUT_LINE ('ORAFCE TEST 1'); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_test(); +DROP FUNCTION dbms_output_test(); diff --git a/src/postgres/src/test/regress/sql/yb_pg_orafce_dbms_pipe_session_A.sql b/src/postgres/src/test/regress/sql/yb_pg_orafce_dbms_pipe_session_A.sql new file mode 100644 index 000000000000..8ecf41a6742f --- /dev/null +++ b/src/postgres/src/test/regress/sql/yb_pg_orafce_dbms_pipe_session_A.sql @@ -0,0 +1,206 @@ +\set ECHO none +SET client_min_messages = warning; +DROP TABLE IF EXISTS TEMP; +CREATE TABLE TEMP(id integer,name text); +INSERT INTO TEMP VALUES (1,'bob'),(2,'rob'),(3,'john'); + +DROP USER IF EXISTS pipe_test_owner; +CREATE ROLE pipe_test_owner WITH CREATEROLE; +ALTER TABLE TEMP OWNER TO pipe_test_owner; +SET client_min_messages = notice; + +-- Notify session B of 'pipe_test_owner' having been created. +SELECT dbms_pipe.pack_message(1); +SELECT dbms_pipe.send_message('pipe_test_owner_created_notifier'); +-- Create a new connection under the userid of pipe_test_owner +SET SESSION AUTHORIZATION pipe_test_owner; + +/* create an implicit pipe and sends message using + * send_message(text,integer,integer) + */ +CREATE OR REPLACE FUNCTION send(pipename text) RETURNS void AS $$ +BEGIN + IF dbms_pipe.send_message(pipename,2,10) = 1 THEN + RAISE NOTICE 'Timeout'; + PERFORM pg_sleep(2); + PERFORM dbms_pipe.send_message(pipename,2,10); + END IF; +END; $$ LANGUAGE plpgsql; + +-- Test pack_message for all supported types and send_message +CREATE OR REPLACE FUNCTION createImplicitPipe() RETURNS void AS $$ +DECLARE + row TEMP%ROWTYPE; +BEGIN + PERFORM dbms_pipe.pack_message('Message From Session A'::text); + PERFORM send('named_pipe'); + PERFORM dbms_pipe.pack_message('2013-01-01'::date); + PERFORM send('named_pipe'); + PERFORM dbms_pipe.pack_message('2013-01-01 09:00:00'::timestamp); + PERFORM send('named_pipe'); + PERFORM dbms_pipe.pack_message('2013-01-01 09:00:00-08'::timestamptz); + PERFORM send('named_pipe'); + PERFORM dbms_pipe.pack_message(12345.6789::numeric); + PERFORM send('named_pipe'); + PERFORM dbms_pipe.pack_message(12345::integer); + PERFORM send('named_pipe'); + PERFORM dbms_pipe.pack_message(99999999999::bigint); + PERFORM send('named_pipe'); + PERFORM dbms_pipe.pack_message(E'\\201'::bytea); + PERFORM send('named_pipe'); + SELECT * INTO row FROM TEMP WHERE id=2; + PERFORM dbms_pipe.pack_message(row); + PERFORM send('named_pipe'); +END; $$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION bulkSend() RETURNS void AS $$ +DECLARE + row TEMP%ROWTYPE; +BEGIN + PERFORM dbms_pipe.pack_message('Message From Session A'::text); + PERFORM dbms_pipe.pack_message('2013-01-01'::date); + PERFORM dbms_pipe.pack_message('2013-01-01 09:00:00'::timestamp); + PERFORM dbms_pipe.pack_message('2013-01-01 09:00:00-08'::timestamptz); + PERFORM dbms_pipe.pack_message(12345.6789::numeric); + PERFORM dbms_pipe.pack_message(12345::integer); + PERFORM dbms_pipe.pack_message(99999999999::bigint); + PERFORM dbms_pipe.pack_message(E'\\201'::bytea); + SELECT * INTO row FROM TEMP WHERE id=2; + PERFORM dbms_pipe.pack_message(row); + PERFORM send('named_pipe_2'); +END; $$ LANGUAGE plpgsql; + + +/* Creates an explicit pipe using either create_pipe(text,integer,bool), + * create_pipe(text,integer) OR create_pipe(text). + * In case third parameter (bool) absent, default is false, that is, it's a public pipe. + */ +CREATE OR REPLACE FUNCTION createPipe(name text,ver integer) RETURNS void AS $$ +BEGIN + IF ver = 3 THEN + PERFORM dbms_pipe.create_pipe(name,4,true); + ELSIF ver = 2 THEN + PERFORM dbms_pipe.create_pipe(name,4); + ELSE + PERFORM dbms_pipe.create_pipe(name); + END IF; +END; $$ LANGUAGE plpgsql; + + +/* Testing create_pipe for different versions, one of them, is the case of + * private pipe + */ + +CREATE OR REPLACE FUNCTION createExplicitPipe(pipename text,create_version integer) RETURNS void AS $$ +DECLARE + row TEMP%ROWTYPE; +BEGIN + + PERFORM createPipe(pipename,create_version); + + PERFORM dbms_pipe.reset_buffer(); + + PERFORM dbms_pipe.pack_message('Message From Session A'::text); + PERFORM send(pipename); + PERFORM dbms_pipe.pack_message('2013-01-01'::date); + PERFORM send(pipename); + PERFORM dbms_pipe.pack_message('2013-01-01 09:00:00'::timestamp); + PERFORM send(pipename); + PERFORM dbms_pipe.pack_message('2013-01-01 09:00:00-08'::timestamptz); + PERFORM send(pipename); + PERFORM dbms_pipe.pack_message(12345.6789::numeric); + PERFORM send(pipename); + PERFORM dbms_pipe.pack_message(12345::integer); + PERFORM send(pipename); + PERFORM dbms_pipe.pack_message(99999999999::bigint); + PERFORM send(pipename); + PERFORM dbms_pipe.pack_message(E'\\201'::bytea); + PERFORM send(pipename); + SELECT * INTO row FROM TEMP WHERE id=2; + PERFORM dbms_pipe.pack_message(row); + PERFORM send(pipename); +END; $$ LANGUAGE plpgsql; + + +-- Test send_message(text) +CREATE OR REPLACE FUNCTION checkSend1() RETURNS void AS $$ +BEGIN + PERFORM dbms_pipe.pack_message('checking one-argument send_message()'); + PERFORM dbms_pipe.send_message('pipe_name_1'); +END; $$ LANGUAGE plpgsql; + + +-- Test send_message(text,integer) +CREATE OR REPLACE FUNCTION checkSend2() RETURNS void AS $$ +BEGIN + PERFORM dbms_pipe.pack_message('checking two-argument send_message()'); + IF dbms_pipe.send_message('pipe_name_2',2) = 1 THEN + RAISE NOTICE 'Timeout'; + PERFORM pg_sleep(2); + PERFORM dbms_pipe.send_message('pipe_name_2',2); + END IF; +END; $$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION notifyDropTemp() RETURNS void AS $$ +BEGIN + PERFORM dbms_pipe.pack_message(1); + PERFORM dbms_pipe.send_message('pipe_name_3'); +END; $$ LANGUAGE plpgsql; + + +CREATE OR REPLACE FUNCTION checkUniqueSessionNameA() RETURNS void AS $$ +BEGIN + PERFORM dbms_pipe.pack_message(dbms_pipe.unique_session_name()); + PERFORM dbms_pipe.send_message('pipe_name_4'); +END; $$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION notify(pipename text) RETURNS void AS $$ +BEGIN + PERFORM dbms_pipe.pack_message(1); + PERFORM dbms_pipe.send_message(pipename); +END; $$ LANGUAGE plpgsql; + +\set ECHO all + +SELECT createImplicitPipe(); + +-- Bulk send messages +SELECT bulkSend(); + +-- An explicit private pipe +SELECT notify('recv_private1_notifier'); +SELECT createExplicitPipe('private_pipe_1',3); + +-- An explicit private pipe +SELECT notify('recv_private2_notifier'); +SELECT createExplicitPipe('private_pipe_2',3); + +-- An explicit public pipe (uses two-argument create_pipe) +SELECT notify('recv_public1_notifier'); +SELECT createExplicitPipe('public_pipe_3',2); + +-- An explicit public pipe (uses one-argument create_pipe) +SELECT notify('recv_public2_notifier'); +SELECT createExplicitPipe('public_pipe_4',1); + +-- tests send_message(text) +SELECT checkSend1(); + +-- tests send_message(text,integer) +SELECT checkSend2(); + +SELECT notifyDropTemp(); + +-- tests unique_session_name() +SELECT checkUniqueSessionNameA(); + +DROP FUNCTION createImplicitPipe(); +DROP FUNCTION createExplicitPipe(text,integer); +DROP FUNCTION createPipe(text,integer); +DROP FUNCTION checkSend1(); +DROP FUNCTION checkSend2(); +DROP FUNCTION checkUniqueSessionNameA(); +DROP FUNCTION bulkSend(); +DROP FUNCTION notifyDropTemp(); +DROP FUNCTION notify(text); +DROP FUNCTION send(text); diff --git a/src/postgres/src/test/regress/sql/yb_pg_orafce_dbms_pipe_session_B.sql b/src/postgres/src/test/regress/sql/yb_pg_orafce_dbms_pipe_session_B.sql new file mode 100644 index 000000000000..0d3cad7326ee --- /dev/null +++ b/src/postgres/src/test/regress/sql/yb_pg_orafce_dbms_pipe_session_B.sql @@ -0,0 +1,165 @@ +\set ECHO none +\set VERBOSITY terse +--Wait for 'pipe_test_owner' created notification to be sent by session A +SELECT dbms_pipe.receive_message('pipe_test_owner_created_notifier'); + +-- create new connection under the userid of 'pipe_test_owner' +SET SESSION AUTHORIZATION pipe_test_owner; + +/* Tests receive_message(text,integer), next_item_type() and all versions of + * unpack_message_() and purge(text) + */ + +CREATE OR REPLACE FUNCTION receiveFrom(pipename text) RETURNS void AS $$ +DECLARE + typ INTEGER; +BEGIN + WHILE true LOOP + PERFORM dbms_pipe.receive_message(pipename,2); + SELECT dbms_pipe.next_item_type() INTO typ; + IF typ = 0 THEN EXIT; + ELSIF typ=9 THEN RAISE NOTICE 'RECEIVE %: %', typ, dbms_pipe.unpack_message_number(); + ELSIF typ=11 THEN RAISE NOTICE 'RECEIVE %: %', typ, dbms_pipe.unpack_message_text(); + ELSIF typ=12 THEN RAISE NOTICE 'RECEIVE %: %', typ, dbms_pipe.unpack_message_date(); + ELSIF typ=13 THEN RAISE NOTICE 'RECEIVE %: %', typ, dbms_pipe.unpack_message_timestamp(); + ELSIF typ=23 THEN RAISE NOTICE 'RECEIVE %: %', typ, encode(dbms_pipe.unpack_message_bytea(),'escape'); + ELSIF typ=24 THEN RAISE NOTICE 'RECEIVE %: %', typ, dbms_pipe.unpack_message_record(); + END IF; + END LOOP; + PERFORM dbms_pipe.purge(pipename); +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION bulkReceive() RETURNS void AS $$ +DECLARE + typ INTEGER; +BEGIN + IF dbms_pipe.receive_message('named_pipe_2',2) = 1 THEN + RAISE NOTICE 'Timeout'; + PERFORM pg_sleep(2); + PERFORM dbms_pipe.receive_message('named_pipe_2',2); + END IF; + WHILE true LOOP + SELECT dbms_pipe.next_item_type() INTO typ; + IF typ = 0 THEN EXIT; + ELSIF typ=9 THEN RAISE NOTICE 'RECEIVE %: %', typ, dbms_pipe.unpack_message_number(); + ELSIF typ=11 THEN RAISE NOTICE 'RECEIVE %: %', typ, dbms_pipe.unpack_message_text(); + ELSIF typ=12 THEN RAISE NOTICE 'RECEIVE %: %', typ, dbms_pipe.unpack_message_date(); + ELSIF typ=13 THEN RAISE NOTICE 'RECEIVE %: %', typ, dbms_pipe.unpack_message_timestamp(); + ELSIF typ=23 THEN RAISE NOTICE 'RECEIVE %: %', typ, encode(dbms_pipe.unpack_message_bytea()::bytea,'escape'); + ELSIF typ=24 THEN RAISE NOTICE 'RECEIVE %: %', typ, dbms_pipe.unpack_message_record(); + END IF; + END LOOP; + PERFORM dbms_pipe.purge('named_pipe_2'); +END; +$$ LANGUAGE plpgsql; + +-- Tests receive_message(text) + +CREATE OR REPLACE FUNCTION checkReceive1(pipename text) RETURNS void AS $$ +BEGIN + PERFORM dbms_pipe.receive_message(pipename); + RAISE NOTICE 'RECEIVE %',dbms_pipe.unpack_message_text(); +END; $$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION dropTempTable() RETURNS void AS $$ +BEGIN + WHILE dbms_pipe.receive_message('pipe_name_3') <> 0 LOOP + CONTINUE; + END LOOP; + DROP TABLE TEMP; +END; $$ LANGUAGE plpgsql; + + +CREATE OR REPLACE FUNCTION checkUniqueSessionNameB() RETURNS bool AS $$ +DECLARE + result bool; +BEGIN + PERFORM dbms_pipe.receive_message('pipe_name_4'); + SELECT dbms_pipe.unpack_message_text() = dbms_pipe.unique_session_name() INTO result; + RETURN result; +END; $$ LANGUAGE plpgsql; + +\set ECHO all + +-- Receives messages sent via an implicit pipe +SELECT receiveFrom('named_pipe'); + +-- Bulk receive messages +SELECT bulkReceive(); + +-- Receives messages sent via an explicit private pipe under the same user +-- 'pipe_test_owner' +SELECT dbms_pipe.receive_message('recv_private1_notifier'); +SELECT receiveFrom('private_pipe_1'); + +-- Switch user to 'pipe_test_other' +DROP USER IF EXISTS pipe_test_other; +CREATE USER pipe_test_other; +SET SESSION AUTHORIZATION pipe_test_other; + +-- Try to receive messages sent via an explicit private pipe under the user +-- 'pipe_test_other' who is not the owner of pipe. +-- insufficient privileges in case of 'private_pipe_2'. + +SELECT dbms_pipe.receive_message('recv_private2_notifier'); +SELECT receiveFrom('private_pipe_2'); + +-- These are explicit private pipes created using create_pipe(text,integer) +-- and create_pipe(text) +SELECT dbms_pipe.receive_message('recv_public1_notifier'); +SELECT receiveFrom('public_pipe_3'); + +SELECT dbms_pipe.receive_message('recv_public2_notifier'); +SELECT receiveFrom('public_pipe_4'); + +-- Switch back to user 'pipe_test_owner' +SET SESSION AUTHORIZATION pipe_test_owner; +DROP USER pipe_test_other; + +-- Tests receive_message(text) +SELECT checkReceive1('pipe_name_1'); +SELECT checkReceive1('pipe_name_2'); + +-- Tests dbms_pipe.db_pipes view +SELECT name, items, "limit", private, owner +FROM dbms_pipe.db_pipes +WHERE name LIKE 'private%' +ORDER BY name; + +-- Tests dbms_pipe.__list_pipes(); attribute size is not included +-- since it can be different across runs. +SELECT name, items, "limit", private, owner +FROM dbms_pipe.__list_pipes() AS (name varchar, items int4, siz int4, "limit" int4, private bool, owner varchar) +WHERE name <> 'pipe_name_4' +ORDER BY 1; + +-- Tests remove_pipe(text) +SELECT dbms_pipe.remove_pipe('private_pipe_1'); +SELECT dbms_pipe.remove_pipe('private_pipe_2'); +SELECT dbms_pipe.remove_pipe('public_pipe_3'); +SELECT dbms_pipe.remove_pipe('public_pipe_4'); +SELECT dbms_pipe.purge('pipe_name_1'); +SELECT dbms_pipe.purge('pipe_name_2'); + +-- Receives drop table notification from session A via 'pipe_name_3' +SELECT dropTempTable(); +SELECT dbms_pipe.purge('pipe_name_3'); + + +-- tests unique_session_name() (uses 'pipe_name_4') +SELECT checkUniqueSessionNameB(); +SELECT dbms_pipe.purge('pipe_name_4'); + +DROP FUNCTION receiveFrom(text); +DROP FUNCTION checkReceive1(text); +DROP FUNCTION checkUniqueSessionNameB(); +DROP FUNCTION bulkReceive(); +DROP FUNCTION dropTempTable(); + +-- Perform a recieve on removed pipe resulting on timeout +SELECT dbms_pipe.receive_message('public_pipe_4',2); +SELECT dbms_pipe.purge('public_pipe_4'); + +SET SESSION AUTHORIZATION DEFAULT; +DROP USER pipe_test_owner; diff --git a/src/postgres/src/test/regress/sql/yb_pg_orafce_dbms_random.sql b/src/postgres/src/test/regress/sql/yb_pg_orafce_dbms_random.sql new file mode 100644 index 000000000000..c773d7ebd939 --- /dev/null +++ b/src/postgres/src/test/regress/sql/yb_pg_orafce_dbms_random.sql @@ -0,0 +1,15 @@ +-- Tests for package DBMS_RANDOM +SELECT dbms_random.initialize(8); +SELECT dbms_random.normal()::numeric(10, 8) != dbms_random.normal()::numeric(10, 8); +SELECT dbms_random.seed(8); +SELECT dbms_random.random() != dbms_random.random(); +SELECT dbms_random.seed('test'); +SELECT dbms_random.string('U',5) != dbms_random.string('U',5); +SELECT dbms_random.string('U',5) != dbms_random.string('P',2); +SELECT dbms_random.string('U',5) != dbms_random.string('x',4); +SELECT dbms_random.string('U',5) != dbms_random.string('a',2); +SELECT dbms_random.string('U',5) != dbms_random.string('l',3); +SELECT dbms_random.seed(5); +SELECT dbms_random.value()::numeric(10, 8) != dbms_random.value()::numeric(10, 8); +SELECT dbms_random.value(10,15)::numeric(10, 8) != dbms_random.value(10,15)::numeric(10, 8);; +SELECT dbms_random.terminate(); diff --git a/src/postgres/src/test/regress/sql/yb_pg_orafce_dbms_utility.sql b/src/postgres/src/test/regress/sql/yb_pg_orafce_dbms_utility.sql new file mode 100644 index 000000000000..7e87db5f661c --- /dev/null +++ b/src/postgres/src/test/regress/sql/yb_pg_orafce_dbms_utility.sql @@ -0,0 +1,63 @@ +\set ECHO none +\pset format unaligned +/* + * Test for dbms_utility.format_call_stack(char mode). + * Mode is hex. + * The callstack returned is passed to regex_replace function. + * Regex_replace replaces the function oid from the stack with zero. + * This is done to avoid random results due to different oids generated. + * Also the line number and () of the function is removed since it is different + * across different pg version. + */ + +CREATE OR REPLACE FUNCTION checkHexCallStack() returns text as $$ + DECLARE + stack text; + BEGIN + select * INTO stack from dbms_utility.format_call_stack('o'); + select * INTO stack from regexp_replace(stack,'[ 0-9a-fA-F]{4}[0-9a-fA-F]{4}',' 0','g'); + select * INTO stack from regexp_replace(stack,'[45()]','','g'); + return stack; + END; +$$ LANGUAGE plpgsql; + +/* + * Test for dbms_utility.format_call_stack(char mode). + * Mode is integer. + */ + +CREATE OR REPLACE FUNCTION checkIntCallStack() returns text as $$ + DECLARE + stack text; + BEGIN + select * INTO stack from dbms_utility.format_call_stack('p'); + select * INTO stack from regexp_replace(stack,'[ 0-9]{3}[0-9]{5}',' 0','g'); + select * INTO stack from regexp_replace(stack,'[45()]','','g'); + return stack; + END; +$$ LANGUAGE plpgsql; + +/* + * Test for dbms_utility.format_call_stack(char mode). + * Mode is integer with unpadded output. + */ + +CREATE OR REPLACE FUNCTION checkIntUnpaddedCallStack() returns text as $$ + DECLARE + stack text; + BEGIN + select * INTO stack from dbms_utility.format_call_stack('s'); + select * INTO stack from regexp_replace(stack,'[0-9]{5,}','0','g'); + select * INTO stack from regexp_replace(stack,'[45()]','','g'); + return stack; + END; +$$ LANGUAGE plpgsql; + +select * from checkHexCallStack(); +select * from checkIntCallStack(); +select * from checkIntUnpaddedCallStack(); + +DROP FUNCTION checkHexCallStack(); +DROP FUNCTION checkIntCallStack(); +DROP FUNCTION checkIntUnpaddedCallStack(); + diff --git a/src/postgres/src/test/regress/sql/yb_pg_orafce_files.sql b/src/postgres/src/test/regress/sql/yb_pg_orafce_files.sql new file mode 100644 index 000000000000..1082b4fe6a3e --- /dev/null +++ b/src/postgres/src/test/regress/sql/yb_pg_orafce_files.sql @@ -0,0 +1,138 @@ +SET client_min_messages = NOTICE; +\set VERBOSITY terse +\set ECHO all +CREATE OR REPLACE FUNCTION gen_file(dir text) RETURNS void AS $$ +DECLARE + f utl_file.file_type; +BEGIN + f := utl_file.fopen(dir, 'regress_orafce.txt', 'w'); + PERFORM utl_file.put_line(f, 'ABC'); + PERFORM utl_file.put_line(f, '123'::numeric); + PERFORM utl_file.put_line(f, '-----'); + PERFORM utl_file.new_line(f); + PERFORM utl_file.put_line(f, '-----'); + PERFORM utl_file.new_line(f, 0); + PERFORM utl_file.put_line(f, '-----'); + PERFORM utl_file.new_line(f, 2); + PERFORM utl_file.put_line(f, '-----'); + PERFORM utl_file.put(f, 'A'); + PERFORM utl_file.put(f, 'B'); + PERFORM utl_file.new_line(f); + PERFORM utl_file.putf(f, '[1=%s, 2=%s, 3=%s, 4=%s, 5=%s]', '1', '2', '3', '4', '5'); + PERFORM utl_file.new_line(f); + PERFORM utl_file.put_line(f, '1234567890'); + f := utl_file.fclose(f); +END; +$$ LANGUAGE plpgsql; + +/* Test functions utl_file.fflush(utl_file.file_type) and + * utl_file.get_nextline(utl_file.file_type) + * This function tests the positive test case of fflush by reading from the + * file after flushing the contents to the file. + */ +CREATE OR REPLACE FUNCTION checkFlushFile(dir text) RETURNS void AS $$ +DECLARE + f utl_file.file_type; + f1 utl_file.file_type; + ret_val text; + i integer; +BEGIN + f := utl_file.fopen(dir, 'regressflush_orafce.txt', 'a'); + PERFORM utl_file.put_line(f, 'ABC'); + PERFORM utl_file.new_line(f); + PERFORM utl_file.put_line(f, '123'::numeric); + PERFORM utl_file.new_line(f); + PERFORM utl_file.putf(f, '[1=%s, 2=%s, 3=%s, 4=%s, 5=%s]', '1', '2', '3', '4', '5'); + PERFORM utl_file.fflush(f); + f1 := utl_file.fopen(dir, 'regressflush_orafce.txt', 'r'); + ret_val=utl_file.get_nextline(f1); + i:=1; + WHILE ret_val IS NOT NULL LOOP + RAISE NOTICE '[%] >>%<<', i,ret_val; + ret_val := utl_file.get_nextline(f1); + i:=i+1; + END LOOP; + RAISE NOTICE '>>%<<', ret_val; + f1 := utl_file.fclose(f1); + f := utl_file.fclose(f); +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION read_file(dir text) RETURNS void AS $$ +DECLARE + f utl_file.file_type; +BEGIN + f := utl_file.fopen(dir, 'regress_orafce.txt', 'r'); + FOR i IN 1..11 LOOP + RAISE NOTICE '[%] >>%<<', i, utl_file.get_line(f); + END LOOP; + RAISE NOTICE '>>%<<', utl_file.get_line(f, 4); + RAISE NOTICE '>>%<<', utl_file.get_line(f, 4); + RAISE NOTICE '>>%<<', utl_file.get_line(f); + RAISE NOTICE '>>%<<', utl_file.get_line(f); + EXCEPTION + -- WHEN no_data_found THEN, 8.1 plpgsql doesn't know no_data_found + WHEN others THEN + RAISE NOTICE 'finish % ', sqlerrm; + RAISE NOTICE 'is_open = %', utl_file.is_open(f); + PERFORM utl_file.fclose_all(); + RAISE NOTICE 'is_open = %', utl_file.is_open(f); + END; +$$ LANGUAGE plpgsql; + +SELECT EXISTS(SELECT * FROM pg_catalog.pg_class where relname='utl_file_dir') AS exists; + +SELECT EXISTS(SELECT * FROM pg_catalog.pg_type where typname='file_type') AS exists; + +-- Trying to access a file in path not registered +SELECT utl_file.fopen(utl_file.tmpdir(),'sample.txt','r'); + +-- Trying to access file in a non-existent directory +INSERT INTO utl_file.utl_file_dir(dir) VALUES('test_tmp_dir'); +SELECT utl_file.fopen('test_tmp_dir','file.txt.','w'); +DELETE FROM utl_file.utl_file_dir WHERE dir LIKE 'test_tmp_dir'; +-- Add tmpdir() to utl_file_dir table +INSERT INTO utl_file.utl_file_dir(dir) VALUES(utl_file.tmpdir()); + +SELECT count(*) from utl_file.utl_file_dir where dir <> ''; + +-- Trying to access non-existent file +SELECT utl_file.fopen(utl_file.tmpdir(),'non_existent_file.txt','r'); + +--Other test cases +--run this under unprivileged user +CREATE ROLE test_role_files LOGIN; +SET ROLE TO test_role_files; + +-- should to fail, unpriviliged user cannot to change utl_file_dir +INSERT INTO utl_file.utl_file_dir(dir) VALUES('test_tmp_dir'); + +SELECT gen_file(utl_file.tmpdir()); +SELECT fexists FROM utl_file.fgetattr(utl_file.tmpdir(), 'regress_orafce.txt'); +SELECT utl_file.fcopy(utl_file.tmpdir(), 'regress_orafce.txt', utl_file.tmpdir(), 'regress_orafce2.txt'); +SELECT fexists FROM utl_file.fgetattr(utl_file.tmpdir(), 'regress_orafce2.txt'); +SELECT utl_file.frename(utl_file.tmpdir(), 'regress_orafce2.txt', utl_file.tmpdir(), 'regress_orafce.txt', true); +SELECT fexists FROM utl_file.fgetattr(utl_file.tmpdir(), 'regress_orafce.txt'); +SELECT fexists FROM utl_file.fgetattr(utl_file.tmpdir(), 'regress_orafce2.txt'); +SELECT read_file(utl_file.tmpdir()); +SELECT utl_file.fremove(utl_file.tmpdir(), 'regress_orafce.txt'); +SELECT fexists FROM utl_file.fgetattr(utl_file.tmpdir(), 'regress_orafce.txt'); +SELECT checkFlushFile(utl_file.tmpdir()); +SELECT utl_file.fremove(utl_file.tmpdir(), 'regressflush_orafce.txt'); + +SET ROLE TO DEFAULT; +DROP ROLE test_role_files; + +DROP FUNCTION checkFlushFile(text); +DELETE FROM utl_file.utl_file_dir; + +-- try to use named directory +INSERT INTO utl_file.utl_file_dir(dir, dirname) VALUES(utl_file.tmpdir(), 'TMPDIR'); +SELECT gen_file('TMPDIR'); +SELECT read_file('TMPDIR'); +SELECT utl_file.fremove('TMPDIR', 'regress_orafce.txt'); + +DROP FUNCTION gen_file(text); +DROP FUNCTION read_file(text); + +DELETE FROM utl_file.utl_file_dir; diff --git a/src/postgres/src/test/regress/sql/yb_pg_orafce_init.sql b/src/postgres/src/test/regress/sql/yb_pg_orafce_init.sql new file mode 100644 index 000000000000..777d73b3880c --- /dev/null +++ b/src/postgres/src/test/regress/sql/yb_pg_orafce_init.sql @@ -0,0 +1,3 @@ +\set ECHO none +set client_min_messages TO default; +CREATE EXTENSION IF NOT EXISTS orafce; diff --git a/src/postgres/src/test/regress/sql/yb_pg_orafce_nlssort.sql b/src/postgres/src/test/regress/sql/yb_pg_orafce_nlssort.sql new file mode 100644 index 000000000000..39d5ada903ab --- /dev/null +++ b/src/postgres/src/test/regress/sql/yb_pg_orafce_nlssort.sql @@ -0,0 +1,18 @@ +-- Tests for nlssort +-- YB: we do not support SQL_ASCII encoding +\set ECHO none +SET client_min_messages = error; +DROP DATABASE IF EXISTS regression_sort; +CREATE DATABASE regression_sort WITH TEMPLATE = template0 ENCODING='UTF8' LC_COLLATE='C' LC_CTYPE='en_US.UTF-8'; +\c regression_sort +SET client_min_messages = error; +CREATE EXTENSION orafce; +SET client_min_messages = default; +CREATE TABLE test_sort (name TEXT); +INSERT INTO test_sort VALUES ('red'), ('brown'), ('yellow'), ('Purple'); +SELECT * FROM test_sort ORDER BY NLSSORT(name, 'C'); +SELECT * FROM test_sort ORDER BY NLSSORT(name, ''); +SELECT set_nls_sort('invalid'); +SELECT * FROM test_sort ORDER BY NLSSORT(name); +SELECT set_nls_sort(''); +SELECT * FROM test_sort ORDER BY NLSSORT(name); \ No newline at end of file diff --git a/src/postgres/src/test/regress/sql/yb_pg_orafce_nvarchar2.sql b/src/postgres/src/test/regress/sql/yb_pg_orafce_nvarchar2.sql new file mode 100644 index 000000000000..19095edd8b5a --- /dev/null +++ b/src/postgres/src/test/regress/sql/yb_pg_orafce_nvarchar2.sql @@ -0,0 +1,62 @@ +\set VERBOSITY terse +SET client_encoding = utf8; + +-- +-- test type modifier related rules +-- + +-- ERROR (typmod >= 1) +CREATE TABLE bar (a NVARCHAR2(0)); + +-- ERROR (number of typmods = 1) +CREATE TABLE bar (a NVARCHAR2(10, 1)); + +-- OK +CREATE TABLE bar (a VARCHAR(5000)); + +CREATE INDEX ON bar(a); + +-- cleanup +DROP TABLE bar; + +-- OK +CREATE TABLE bar (a NVARCHAR2(5)); + +-- +-- test that no value longer than maxlen is allowed +-- + +-- ERROR (length > 5) +INSERT INTO bar VALUES ('abcdef'); + +-- ERROR (length > 5); +-- NVARCHAR2 does not truncate blank spaces on implicit coercion +INSERT INTO bar VALUES ('abcde '); + +-- OK +INSERT INTO bar VALUES ('abcde'); + +-- OK +INSERT INTO bar VALUES ('abcdef'::NVARCHAR2(5)); + +-- OK +INSERT INTO bar VALUES ('abcde '::NVARCHAR2(5)); + +--OK +INSERT INTO bar VALUES ('abc'::NVARCHAR2(5)); + +-- +-- test whitespace semantics on comparison +-- + +-- equal +SELECT 'abcde '::NVARCHAR2(10) = 'abcde '::NVARCHAR2(10); + +-- not equal +SELECT 'abcde '::NVARCHAR2(10) = 'abcde '::NVARCHAR2(10); + +-- null safe concat (disabled by default) +SELECT NULL || 'hello'::varchar2 || NULL; + +SET orafce.varchar2_null_safe_concat TO true; +SELECT NULL || 'hello'::varchar2 || NULL; diff --git a/src/postgres/src/test/regress/sql/yb_pg_orafce_varchar2.sql b/src/postgres/src/test/regress/sql/yb_pg_orafce_varchar2.sql new file mode 100644 index 000000000000..a496c5262160 --- /dev/null +++ b/src/postgres/src/test/regress/sql/yb_pg_orafce_varchar2.sql @@ -0,0 +1,101 @@ +\set VERBOSITY terse +SET client_encoding = utf8; + +-- +-- test type modifier related rules +-- + +-- ERROR (typmod >= 1) +CREATE TABLE foo (a VARCHAR2(0)); + +-- ERROR (number of typmods = 1) +CREATE TABLE foo (a VARCHAR2(10, 1)); + +-- OK +CREATE TABLE foo (a VARCHAR(5000)); + +-- cleanup +DROP TABLE foo; + +-- OK +CREATE TABLE foo (a VARCHAR2(5)); + +CREATE INDEX ON foo(a); + +-- +-- test that no value longer than maxlen is allowed +-- + +-- ERROR (length > 5) +INSERT INTO foo VALUES ('abcdef'); + +-- ERROR (length > 5); +-- VARCHAR2 does not truncate blank spaces on implicit coercion +INSERT INTO foo VALUES ('abcde '); + +-- OK +INSERT INTO foo VALUES ('abcde'); + +-- OK +INSERT INTO foo VALUES ('abcdef'::VARCHAR2(5)); + +-- OK +INSERT INTO foo VALUES ('abcde '::VARCHAR2(5)); + +--OK +INSERT INTO foo VALUES ('abc'::VARCHAR2(5)); + +-- +-- test whitespace semantics on comparison +-- + +-- equal +SELECT 'abcde '::VARCHAR2(10) = 'abcde '::VARCHAR2(10); + +-- not equal +SELECT 'abcde '::VARCHAR2(10) = 'abcde '::VARCHAR2(10); + +-- +-- test string functions created for varchar2 +-- + +-- substrb(varchar2, int, int) +SELECT substrb('ABCありがとう'::VARCHAR2, 7, 6); + +-- returns 'f' (emtpy string is not NULL) +SELECT substrb('ABCありがとう'::VARCHAR2, 7, 0) IS NULL; + +-- If the starting position is zero or less, then return from the start +-- of the string adjusting the length to be consistent with the "negative start" +-- per SQL. +SELECT substrb('ABCありがとう'::VARCHAR2, 0, 4); + +-- substrb(varchar2, int) +SELECT substrb('ABCありがとう', 5); + +-- strposb(varchar2, varchar2) +SELECT strposb('ABCありがとう', 'りが'); + +-- returns 1 (start of the source string) +SELECT strposb('ABCありがとう', ''); + +-- returns 0 +SELECT strposb('ABCありがとう', 'XX'); + +-- returns 't' +SELECT strposb('ABCありがとう', NULL) IS NULL; + +-- lengthb(varchar2) +SELECT lengthb('ABCありがとう'); + +-- returns 0 +SELECT lengthb(''); + +-- returs 't' +SELECT lengthb(NULL) IS NULL; + +-- null safe concat (disabled by default) +SELECT NULL || 'hello'::varchar2 || NULL; + +SET orafce.varchar2_null_safe_concat TO true; +SELECT NULL || 'hello'::varchar2 || NULL; diff --git a/src/postgres/src/test/regress/yb_extensions_schedule b/src/postgres/src/test/regress/yb_extensions_schedule index ce036f293fe4..6a8d3e3a8d08 100644 --- a/src/postgres/src/test/regress/yb_extensions_schedule +++ b/src/postgres/src/test/regress/yb_extensions_schedule @@ -7,3 +7,19 @@ test: yb_extensions test: yb_pgaudit test: yb_pg_hint_plan_init test: yb_pg_hint_plan_test1 +test: yb_pg_orafce_init +test: yb_pg_orafce +test: yb_pg_orafce2 +test: yb_pg_orafce_aggregates +test: yb_pg_orafce_dbms_alert_session_A +test: yb_pg_orafce_dbms_alert_session_B +test: yb_pg_orafce_dbms_alert_session_C +test: yb_pg_orafce_dbms_output +test: yb_pg_orafce_dbms_pipe_session_A +test: yb_pg_orafce_dbms_pipe_session_B +test: yb_pg_orafce_dbms_random +test: yb_pg_orafce_dbms_utility +test: yb_pg_orafce_files +test: yb_pg_orafce_nlssort +test: yb_pg_orafce_nvarchar2 +test: yb_pg_orafce_varchar2 diff --git a/src/postgres/third-party-extensions/Makefile b/src/postgres/third-party-extensions/Makefile index 06c914b11779..854d64594dfb 100644 --- a/src/postgres/third-party-extensions/Makefile +++ b/src/postgres/third-party-extensions/Makefile @@ -1,4 +1,4 @@ -DIRS = pg_hint_plan +DIRS = pg_hint_plan orafce BUILDDIRS = $(DIRS:%=build-%) INSTALLDIRS = $(DIRS:%=install-%) CLEANDIRS = $(DIRS:%=clean-%) diff --git a/src/postgres/third-party-extensions/orafce/COPYRIGHT.orafce b/src/postgres/third-party-extensions/orafce/COPYRIGHT.orafce new file mode 100644 index 000000000000..6cae1c8b7531 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/COPYRIGHT.orafce @@ -0,0 +1,14 @@ +0-clause license ("Zero Clause BSD") + +Copyright (C) 2008-2020 by Pavel Stehule + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/src/postgres/third-party-extensions/orafce/Makefile b/src/postgres/third-party-extensions/orafce/Makefile new file mode 100644 index 000000000000..929c6dfc48cf --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/Makefile @@ -0,0 +1,56 @@ +MODULE_big = orafce +OBJS= parse_keyword.o convert.o file.o datefce.o magic.o others.o plvstr.o plvdate.o shmmc.o plvsubst.o utility.o plvlex.o alert.o pipe.o sqlparse.o putline.o assert.o plunit.o random.o aggregate.o orafce.o varchar2.o nvarchar2.o charpad.o charlen.o replace_empty_string.o + +EXTENSION = orafce + +DATA = orafce--*.sql + +# make "all" the default target +all: + +REGRESS = orafce orafce2 dbms_output dbms_utility files varchar2 nvarchar2 aggregates nlssort dbms_random + +REGRESS_OPTS = --schedule=parallel_schedule --encoding=utf8 + +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) + +ifeq ($(enable_nls), yes) +SHLIB_LINK += $(filter -lintl,$(LIBS)) +endif + +# remove dependency to libxml2 and libxslt +LIBS := $(filter-out -lxml2, $(LIBS)) +LIBS := $(filter-out -lxslt, $(LIBS)) + +plvlex.o: sqlparse.o + +sqlparse.o: $(srcdir)/sqlscan.c + +$(srcdir)/sqlparse.h: $(srcdir)/sqlparse.c ; + +$(srcdir)/sqlparse.c: sqlparse.y +ifdef BISON + $(BISON) -d $(BISONFLAGS) -o $@ $< +else +ifdef YACC + $(YACC) -d $(YFLAGS) -p cube_yy $< + mv -f y.tab.c sqlparse.c + mv -f y.tab.h sqlparse.h +else + bison -d $(BISONFLAGS) -o $@ $< +endif +endif + +$(srcdir)/sqlscan.c: sqlscan.l +ifdef FLEX + $(FLEX) $(FLEXFLAGS) -o'$@' $< +else + flex $(FLEXFLAGS) -o'$@' $< +endif + +distprep: $(srcdir)/sqlparse.c $(srcdir)/sqlscan.c + +maintainer-clean: + rm -f $(srcdir)/sqlparse.c $(srcdir)/sqlscan.c $(srcdir)/sqlparse.h $(srcdir)/y.tab.c $(srcdir)/y.tab.h diff --git a/src/postgres/third-party-extensions/orafce/aggregate.c b/src/postgres/third-party-extensions/orafce/aggregate.c new file mode 100644 index 000000000000..beb2a3cc5625 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/aggregate.c @@ -0,0 +1,374 @@ +#include "postgres.h" + +#include + +#include "funcapi.h" +#include "builtins.h" + +#include "lib/stringinfo.h" +#include "utils/builtins.h" + +#include "orafce.h" + +PG_FUNCTION_INFO_V1(orafce_listagg1_transfn); +PG_FUNCTION_INFO_V1(orafce_wm_concat_transfn); +PG_FUNCTION_INFO_V1(orafce_listagg2_transfn); +PG_FUNCTION_INFO_V1(orafce_listagg_finalfn); + +PG_FUNCTION_INFO_V1(orafce_median4_transfn); +PG_FUNCTION_INFO_V1(orafce_median4_finalfn); +PG_FUNCTION_INFO_V1(orafce_median8_transfn); +PG_FUNCTION_INFO_V1(orafce_median8_finalfn); + +typedef struct +{ + int alen; /* allocated length */ + int nextlen; /* next allocated length */ + int nelems; /* number of valid entries */ + union + { + float4 *float4_values; + float8 *float8_values; + } d; +} MedianState; + +int orafce_float4_cmp(const void *a, const void *b); +int orafce_float8_cmp(const void *a, const void *b); + +/**************************************************************** + * listagg + * + * Concates values and returns string. + * + * Syntax: + * FUNCTION listagg(string varchar, delimiter varchar = '') + * RETURNS varchar; + * + * Note: any NULL value is ignored. + * + ****************************************************************/ +/* subroutine to initialize state */ +static StringInfo +makeStringAggState(FunctionCallInfo fcinfo) +{ + StringInfo state; + MemoryContext aggcontext; + MemoryContext oldcontext; + + if (!AggCheckCallContext(fcinfo, &aggcontext)) + { + /* cannot be called directly because of internal-type argument */ + elog(ERROR, "listagg_transfn called in non-aggregate context"); + } + + /* + * Create state in aggregate context. It'll stay there across subsequent + * calls. + */ + oldcontext = MemoryContextSwitchTo(aggcontext); + state = makeStringInfo(); + MemoryContextSwitchTo(oldcontext); + + return state; +} + +static void +appendStringInfoText(StringInfo str, const text *t) +{ + appendBinaryStringInfo(str, VARDATA_ANY(t), VARSIZE_ANY_EXHDR(t)); +} + +Datum +orafce_listagg1_transfn(PG_FUNCTION_ARGS) +{ + StringInfo state; + + state = PG_ARGISNULL(0) ? NULL : (StringInfo) PG_GETARG_POINTER(0); + + /* Append the element unless null. */ + if (!PG_ARGISNULL(1)) + { + if (state == NULL) + state = makeStringAggState(fcinfo); + appendStringInfoText(state, PG_GETARG_TEXT_PP(1)); /* value */ + } + + /* + * The transition type for string_agg() is declared to be "internal", + * which is a pass-by-value type the same size as a pointer. + */ + PG_RETURN_POINTER(state); +} + +Datum +orafce_wm_concat_transfn(PG_FUNCTION_ARGS) +{ + StringInfo state; + + state = PG_ARGISNULL(0) ? NULL : (StringInfo) PG_GETARG_POINTER(0); + + /* Append the element unless null. */ + if (!PG_ARGISNULL(1)) + { + if (state == NULL) + state = makeStringAggState(fcinfo); + else + appendStringInfoChar(state, ','); + + appendStringInfoText(state, PG_GETARG_TEXT_PP(1)); /* value */ + } + + /* + * The transition type for string_agg() is declared to be "internal", + * which is a pass-by-value type the same size as a pointer. + */ + PG_RETURN_POINTER(state); +} + + +Datum +orafce_listagg2_transfn(PG_FUNCTION_ARGS) +{ + return string_agg_transfn(fcinfo); +} + +Datum +orafce_listagg_finalfn(PG_FUNCTION_ARGS) +{ + return string_agg_finalfn(fcinfo); +} + +static MedianState * +accumFloat4(MedianState *mstate, float4 value, MemoryContext aggcontext) +{ + MemoryContext oldcontext; + + if (mstate == NULL) + { + /* First call - initialize */ + oldcontext = MemoryContextSwitchTo(aggcontext); + mstate = palloc(sizeof(MedianState)); + mstate->alen = 1024; + mstate->nextlen = 2 * 1024; + mstate->nelems = 0; + mstate->d.float4_values = palloc(mstate->alen * sizeof(float4)); + MemoryContextSwitchTo(oldcontext); + } + else + { + /* enlarge float4_values if needed */ + if (mstate->nelems >= mstate->alen) + { + int newlen = mstate->nextlen; + + oldcontext = MemoryContextSwitchTo(aggcontext); + mstate->nextlen += mstate->alen; + mstate->alen = newlen; + mstate->d.float4_values = repalloc(mstate->d.float4_values, + mstate->alen * sizeof(float4)); + MemoryContextSwitchTo(oldcontext); + } + } + + mstate->d.float4_values[mstate->nelems++] = value; + + return mstate; +} + +static MedianState * +accumFloat8(MedianState *mstate, float8 value, MemoryContext aggcontext) +{ + MemoryContext oldcontext; + + if (mstate == NULL) + { + /* First call - initialize */ + oldcontext = MemoryContextSwitchTo(aggcontext); + mstate = palloc(sizeof(MedianState)); + mstate->alen = 1024; + mstate->nextlen = 2 * 1024; + mstate->nelems = 0; + mstate->d.float8_values = palloc(mstate->alen * sizeof(float8)); + MemoryContextSwitchTo(oldcontext); + } + else + { + /* enlarge float4_values if needed */ + if (mstate->nelems >= mstate->alen) + { + int newlen = mstate->nextlen; + + oldcontext = MemoryContextSwitchTo(aggcontext); + mstate->nextlen += mstate->alen; + mstate->alen = newlen; + mstate->d.float8_values = repalloc(mstate->d.float8_values, + mstate->alen * sizeof(float8)); + MemoryContextSwitchTo(oldcontext); + } + } + + mstate->d.float8_values[mstate->nelems++] = value; + + return mstate; +} + +Datum +orafce_median4_transfn(PG_FUNCTION_ARGS) +{ + MemoryContext aggcontext; + MedianState *state = NULL; + float4 elem; + + if (!AggCheckCallContext(fcinfo, &aggcontext)) + { + /* cannot be called directly because of internal-type argument */ + elog(ERROR, "median4_transfn called in non-aggregate context"); + } + + state = PG_ARGISNULL(0) ? NULL : (MedianState *) PG_GETARG_POINTER(0); + if (PG_ARGISNULL(1)) + PG_RETURN_POINTER(state); + + elem = PG_GETARG_FLOAT4(1); + state = accumFloat4(state, elem, aggcontext); + + PG_RETURN_POINTER(state); +} + +int +orafce_float4_cmp(const void *_a, const void *_b) +{ + float4 a = *((float4 *) _a); + float4 b = *((float4 *) _b); + + if (isnan(a)) + { + if (isnan(b)) + return 0; + else + return 1; + } + else if (isnan(b)) + { + return -1; + } + else + { + if (a > b) + return 1; + else if (a < b) + return -1; + else + return 0; + } +} + +Datum +orafce_median4_finalfn(PG_FUNCTION_ARGS) +{ + MedianState *state = NULL; + int lidx; + int hidx; + float4 result; + + if (PG_ARGISNULL(0)) + PG_RETURN_NULL(); + + state = (MedianState *) PG_GETARG_POINTER(0); + + if (state == NULL) + PG_RETURN_NULL(); + + qsort(state->d.float4_values, state->nelems, sizeof(float4), orafce_float4_cmp); + + lidx = state->nelems / 2 + 1 - 1; + hidx = (state->nelems + 1) / 2 - 1; + + if (lidx == hidx) + result = state->d.float4_values[lidx]; + else + result = (state->d.float4_values[lidx] + state->d.float4_values[hidx]) / 2.0f; + + PG_RETURN_FLOAT4(result); +} + +Datum +orafce_median8_transfn(PG_FUNCTION_ARGS) +{ + MemoryContext aggcontext; + MedianState *state = NULL; + float8 elem; + + if (!AggCheckCallContext(fcinfo, &aggcontext)) + { + /* cannot be called directly because of internal-type argument */ + elog(ERROR, "median4_transfn called in non-aggregate context"); + } + + state = PG_ARGISNULL(0) ? NULL : (MedianState *) PG_GETARG_POINTER(0); + if (PG_ARGISNULL(1)) + PG_RETURN_POINTER(state); + + elem = PG_GETARG_FLOAT8(1); + state = accumFloat8(state, elem, aggcontext); + + PG_RETURN_POINTER(state); +} + +int +orafce_float8_cmp(const void *_a, const void *_b) +{ + float8 a = *((float8 *) _a); + float8 b = *((float8 *) _b); + + if (isnan(a)) + { + if (isnan(b)) + return 0; + else + return 1; + } + else if (isnan(b)) + { + return -1; + } + else + { + if (a > b) + return 1; + else if (a < b) + return -1; + else + return 0; + } +} + + +Datum +orafce_median8_finalfn(PG_FUNCTION_ARGS) +{ + MedianState *state = NULL; + int lidx; + int hidx; + float8 result; + + if (PG_ARGISNULL(0)) + PG_RETURN_NULL(); + + state = (MedianState *) PG_GETARG_POINTER(0); + + if (state == NULL) + PG_RETURN_NULL(); + + qsort(state->d.float8_values, state->nelems, sizeof(float8), orafce_float8_cmp); + + lidx = state->nelems / 2 + 1 - 1; + hidx = (state->nelems + 1) / 2 - 1; + + if (lidx == hidx) + result = state->d.float8_values[lidx]; + else + result = (state->d.float8_values[lidx] + state->d.float8_values[hidx]) / 2.0; + + PG_RETURN_FLOAT8(result); +} diff --git a/src/postgres/third-party-extensions/orafce/alert.c b/src/postgres/third-party-extensions/orafce/alert.c new file mode 100644 index 000000000000..f1475483c671 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/alert.c @@ -0,0 +1,988 @@ +#include "postgres.h" +#include "executor/spi.h" + +#include "access/htup_details.h" +#include "catalog/pg_type.h" +#include "commands/trigger.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "string.h" +#include "storage/lwlock.h" +#include "utils/timestamp.h" + +#include "orafce.h" +#include "builtins.h" +#include "pipe.h" +#include "shmmc.h" +#include "utils/rel.h" + +PG_FUNCTION_INFO_V1(dbms_alert_register); +PG_FUNCTION_INFO_V1(dbms_alert_remove); +PG_FUNCTION_INFO_V1(dbms_alert_removeall); +PG_FUNCTION_INFO_V1(dbms_alert_set_defaults); +PG_FUNCTION_INFO_V1(dbms_alert_signal); +PG_FUNCTION_INFO_V1(dbms_alert_waitany); +PG_FUNCTION_INFO_V1(dbms_alert_waitone); +PG_FUNCTION_INFO_V1(dbms_alert_defered_signal); + +extern int sid; +float8 sensitivity = 250.0; +extern LWLockId shmem_lockid; + +#ifndef _GetCurrentTimestamp +#define _GetCurrentTimestamp() GetCurrentTimestamp() +#endif + +#ifndef GetNowFloat +#ifdef HAVE_INT64_TIMESTAMP +#define GetNowFloat() ((float8) _GetCurrentTimestamp() / 1000000.0) +#else +#define GetNowFloat() _GetCurrentTimestamp() +#endif +#endif + +#define TDAYS (1000*24*3600) + + +/* + * There are maximum 30 events and 255 collaborating sessions + * + */ + +alert_event *events; +alert_lock *locks; + +alert_lock *session_lock = NULL; + +#define NOT_FOUND -1 +#define NOT_USED -1 + +/* + * Compare text and cstr + */ + +static int +textcmpm(text *txt, char *str) +{ + int retval; + char *p; + int len; + + len = VARSIZE(txt) - VARHDRSZ; + p = VARDATA(txt); + + while (len-- && *p != '\0') + { + + if (0 != (retval = *p++ - *str++)) + return retval; + } + + if (len > 0) + return 1; + + if (*str != '\0') + return -1; + + return 0; +} + + +/* + * find or create event rec + * + */ + +static alert_lock* +find_lock(int sid, bool create) +{ + int i; + int first_free = NOT_FOUND; + + if (session_lock != NULL) + return session_lock; + + for (i = 0; i < MAX_LOCKS; i++) + { + if (locks[i].sid == sid) + return &locks[i]; + else if (locks[i].sid == NOT_USED && first_free == NOT_FOUND) + first_free = i; + } + + if (create) + { + if (first_free != NOT_FOUND) + { + locks[first_free].sid = sid; + locks[first_free].echo = NULL; + session_lock = &locks[first_free]; + return &locks[first_free]; + } + else + ereport(ERROR, + (errcode(ERRCODE_ORA_PACKAGES_LOCK_REQUEST_ERROR), + errmsg("lock request error"), + errdetail("Failed to create session lock."), + errhint("There are too many collaborating sessions. Increase MAX_LOCKS in 'pipe.h'."))); + } + + return NULL; +} + + +static alert_event* +find_event(text *event_name, bool create, int *event_id) +{ + int i; + + for (i = 0; i < MAX_EVENTS;i++) + { + if (events[i].event_name != NULL && textcmpm(event_name,events[i].event_name) == 0) + { + if (event_id != NULL) + *event_id = i; + return &events[i]; + } + } + + if (create) + { + for (i=0; i < MAX_EVENTS; i++) + { + if (events[i].event_name == NULL) + { + events[i].event_name = ora_scstring(event_name); + + events[i].max_receivers = 0; + events[i].receivers = NULL; + events[i].messages = NULL; + events[i].receivers_number = 0; + + if (event_id != NULL) + *event_id = i; + return &events[i]; + } + } + + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("event registeration error"), + errdetail("Too many registered events."), + errhint("There are too many collaborating sessions. Increase MAX_EVENTS in 'pipe.h'."))); + } + + return NULL; +} + + +static void +register_event(text *event_name) +{ + alert_event *ev; + int *new_receivers; + int first_free; + int i; + + find_lock(sid, true); + ev = find_event(event_name, true, NULL); + + first_free = NOT_FOUND; + for (i = 0; i < ev->max_receivers; i++) + { + if (ev->receivers[i] == sid) + return; /* event is registered */ + if (ev->receivers[i] == NOT_USED && first_free == NOT_FOUND) + first_free = i; + } + + /* + * I can have a maximum of MAX_LOCKS receivers for one event. + * Array receivers is increased for 16 fields + */ + if (first_free == NOT_FOUND) + { + if (ev->max_receivers + 16 > MAX_LOCKS) + ereport(ERROR, + (errcode(ERRCODE_ORA_PACKAGES_LOCK_REQUEST_ERROR), + errmsg("lock request error"), + errdetail("Failed to create session lock."), + errhint("There are too many collaborating sessions. Increase MAX_LOCKS in 'pipe.h'."))); + + /* increase receiver's array */ + + new_receivers = (int*)salloc((ev->max_receivers + 16)*sizeof(int)); + + for (i = 0; i < ev->max_receivers + 16; i++) + { + if (i < ev->max_receivers) + new_receivers[i] = ev->receivers[i]; + else + new_receivers[i] = NOT_USED; + } + + ev->max_receivers += 16; + if (ev->receivers) + ora_sfree(ev->receivers); + + ev->receivers = new_receivers; + + first_free = ev->max_receivers - 16; + } + + ev->receivers_number += 1; + ev->receivers[first_free] = sid; +} + +/* + * Remove receiver from default receivers of message, + * I expect clean all message_items + */ + +static void +unregister_event(int event_id, int sid) +{ + alert_event *ev; + int i; + + ev = &events[event_id]; + if (ev->receivers_number > 0) + { + for (i = 0; i < ev->max_receivers; i++) + { + if (ev->receivers[i] == sid) + { + ev->receivers[i] = NOT_USED; + ev->receivers_number -= 1; + break; + } + } + if (ev->receivers_number == 0) + { + ora_sfree(ev->receivers); + ora_sfree(ev->event_name); + ev->receivers = NULL; + ev->event_name = NULL; + } + } +} + + +/* + * remove receiver from list of receivers. + * Message has always minimal one receiver + * Return true, if exist other receiver + */ + +static bool +remove_receiver(message_item *msg, int sid) +{ + int i; + bool find_other = false; + bool found = false; + + for (i = 0; i < msg->receivers_number; i++) + { + if (msg->receivers[i] == sid) + { + msg->receivers[i] = NOT_USED; + found = true; + } + else if (msg->receivers[i] != NOT_USED) + { + find_other = true; + } + if (found && find_other) + break; + } + + return find_other; +} + +/* + * + * Reads message message_id for user sid. If arg:all is true, + * then get any message. If arg:remove_all then remove all + * signaled messages for sid. If arg:filter_message then + * skip other messages than message_id, else read and remove + * all others messages than message_id. + * + */ + +static char* +find_and_remove_message_item(int message_id, int sid, + bool all, bool remove_all, + bool filter_message, + int *sleep, char **event_name) +{ + alert_lock *alck; + int _message_id; + + char *result = NULL; + if (sleep != NULL) + *sleep = 0; + + alck = find_lock(sid, false); + + if (event_name) + *event_name = NULL; + + if (alck != NULL && alck->echo != NULL) + { + /* if I have registered and created item */ + struct _message_echo *echo, *last_echo; + + echo = alck->echo; + last_echo = NULL; + + while (echo != NULL) + { + char *message_text; + bool destroy_msg_item = false; + + if (filter_message && echo->message_id != message_id) + { + last_echo = echo; + echo = echo->next_echo; + continue; + } + + message_text = echo->message->message; + _message_id = echo->message_id; + + if (!remove_receiver(echo->message, sid)) + { + destroy_msg_item = true; + if (echo->message->prev_message != NULL) + echo->message->prev_message->next_message = + echo->message->next_message; + else + events[echo->message_id].messages = + echo->message->next_message; + if (echo->message->next_message != NULL) + echo->message->next_message->prev_message = + echo->message->prev_message; + ora_sfree(echo->message->receivers); + ora_sfree(echo->message); + } + if (last_echo == NULL) + { + alck->echo = echo->next_echo; + ora_sfree(echo); + echo = alck->echo; + } + else + { + last_echo->next_echo = echo->next_echo; + ora_sfree(echo); + echo = last_echo; + } + if (remove_all) + { + if (message_text != NULL && destroy_msg_item) + ora_sfree(message_text); + + continue; + } + else if (_message_id == message_id || all) + { + /* I have to do local copy */ + if (message_text) + { + result = pstrdup(message_text); + if (destroy_msg_item) + ora_sfree(message_text); + } + + if (event_name != NULL) + *event_name = pstrdup(events[_message_id].event_name); + + break; + } + } + } + + return result; +} + +/* + * Queue mustn't to contain duplicate messages + */ + +static void +create_message(text *event_name, text *message) +{ + int event_id; + alert_event *ev; + message_item *msg_item = NULL; + int i,j,k; + + find_event(event_name, false, &event_id); + + /* process event only when any recipient exitsts */ + if (NULL != (ev = find_event(event_name, false, &event_id))) + { + if (ev->receivers_number > 0) + { + msg_item = ev->messages; + while (msg_item != NULL) + { + if (msg_item->message == NULL && message == NULL) + return; + if (msg_item->message != NULL && message != NULL) + if (0 == textcmpm(message,msg_item->message)) + return; + + msg_item = msg_item->next_message; + } + + msg_item = salloc(sizeof(message_item)); + + msg_item->receivers = salloc( ev->receivers_number*sizeof(int)); + msg_item->receivers_number = ev->receivers_number; + + if (message != NULL) + msg_item->message = ora_scstring(message); + else + msg_item->message = NULL; + + msg_item->message_id = event_id; + for (i = j = 0; j < ev->max_receivers; j++) + { + if (ev->receivers[j] != NOT_USED) + { + msg_item->receivers[i++] = ev->receivers[j]; + for (k = 0; k < MAX_LOCKS; k++) + if (locks[k].sid == ev->receivers[j]) + { + /* create echo */ + + message_echo *echo = salloc(sizeof(message_echo)); + echo->message = msg_item; + echo->message_id = event_id; + echo->next_echo = NULL; + + if (locks[k].echo == NULL) + locks[k].echo = echo; + else + { + message_echo *p; + p = locks[k].echo; + + while (p->next_echo != NULL) + p = p->next_echo; + + p->next_echo = echo; + } + } + + } + } + + msg_item->next_message = NULL; + if (ev->messages == NULL) + { + msg_item->prev_message = NULL; + ev->messages = msg_item; + } + else + { + message_item *p; + p = ev->messages; + while (p->next_message != NULL) + p = p->next_message; + + p->next_message = msg_item; + msg_item->prev_message = p; + } + + } + } +} + + +#define WATCH_PRE(t, et, c) \ +et = GetNowFloat() + (float8)t; c = 0; \ +do \ +{ \ + +#define WATCH_POST(t,et,c) \ +if (GetNowFloat() >= et) \ +break; \ +if (cycle++ % 100 == 0) \ +CHECK_FOR_INTERRUPTS(); \ +pg_usleep(10000L); \ +} while(t != 0); + + +/* + * + * PROCEDURE DBMS_ALERT.REGISTER (name IN VARCHAR2); + * + * Registers the calling session to receive notification of alert name. + * + */ + +Datum +dbms_alert_register(PG_FUNCTION_ARGS) +{ + text *name = PG_GETARG_TEXT_P(0); + int cycle = 0; + float8 endtime; + float8 timeout = 2; + + WATCH_PRE(timeout, endtime, cycle); + if (ora_lock_shmem(SHMEMMSGSZ, MAX_PIPES, MAX_EVENTS, MAX_LOCKS, false)) + { + register_event(name); + LWLockRelease(shmem_lockid); + PG_RETURN_VOID(); + } + WATCH_POST(timeout, endtime, cycle); + LOCK_ERROR(); + PG_RETURN_VOID(); +} + + +/* + * + * PROCEDURE DBMS_ALERT.REMOVE(name IN VARCHAR2); + * + * Unregisters the calling session from receiving notification of alert name. + * Don't raise any exceptions. + * + */ + +Datum +dbms_alert_remove(PG_FUNCTION_ARGS) +{ + text *name = PG_GETARG_TEXT_P(0); + + alert_event *ev; + int ev_id; + int cycle = 0; + float8 endtime; + float8 timeout = 2; + + WATCH_PRE(timeout, endtime, cycle); + if (ora_lock_shmem(SHMEMMSGSZ, MAX_PIPES,MAX_EVENTS,MAX_LOCKS,false)) + { + ev = find_event(name, false, &ev_id); + if (NULL != ev) + { + find_and_remove_message_item(ev_id, sid, + false, true, true, NULL, NULL); + unregister_event(ev_id, sid); + } + LWLockRelease(shmem_lockid); + PG_RETURN_VOID(); + } + WATCH_POST(timeout, endtime, cycle); + LOCK_ERROR(); + PG_RETURN_VOID(); +} + + +/* + * + * PROCEDURE DBMS_ALERT.REMOVEALL; + * + * Unregisters the calling session from notification of all alerts. + * + */ + +Datum +dbms_alert_removeall(PG_FUNCTION_ARGS) +{ + int i; + int cycle = 0; + float8 endtime; + float8 timeout = 2; + + WATCH_PRE(timeout, endtime, cycle); + if (ora_lock_shmem(SHMEMMSGSZ, MAX_PIPES,MAX_EVENTS,MAX_LOCKS,false)) + { + for (i = 0; i < MAX_EVENTS; i++) + if (events[i].event_name != NULL) + { + find_and_remove_message_item(i, sid, + false, true, true, NULL, NULL); + unregister_event(i, sid); + + } + LWLockRelease(shmem_lockid); + PG_RETURN_VOID(); + } + WATCH_POST(timeout, endtime, cycle); + LOCK_ERROR(); + PG_RETURN_VOID(); +} + + + +/* + * + * PROCEDURE DBMS_ALERT.WAITANY(name OUT VARCHAR2 ,message OUT VARCHAR2 + * ,status OUT INTEGER + * ,timeout IN NUMBER DEFAULT MAXWAIT); + * + * Waits for up to timeout seconds to be notified of any alerts for which + * the session is registered. If status = 0 then name and message contain + * alert information. If status = 1 then timeout seconds elapsed without + * notification of any alert. + * + */ + +Datum +dbms_alert_waitany(PG_FUNCTION_ARGS) +{ + float8 timeout; + TupleDesc tupdesc; + AttInMetadata *attinmeta; + HeapTuple tuple; + Datum result; + char *str[3] = {NULL, NULL, "1"}; + int cycle = 0; + float8 endtime; + TupleDesc btupdesc; + + if (PG_ARGISNULL(0)) + timeout = TDAYS; + else + timeout = PG_GETARG_FLOAT8(0); + + WATCH_PRE(timeout, endtime, cycle); + if (ora_lock_shmem(SHMEMMSGSZ, MAX_PIPES, MAX_EVENTS, MAX_LOCKS, false)) + { + str[1] = find_and_remove_message_item(-1, sid, + true, false, false, NULL, &str[0]); + if (str[0]) + { + str[2] = "0"; + LWLockRelease(shmem_lockid); + break; + } + LWLockRelease(shmem_lockid); + } + WATCH_POST(timeout, endtime, cycle); + + get_call_result_type(fcinfo, NULL, &tupdesc); + btupdesc = BlessTupleDesc(tupdesc); + attinmeta = TupleDescGetAttInMetadata(btupdesc); + tuple = BuildTupleFromCStrings(attinmeta, str); + result = HeapTupleGetDatum(tuple); + + if (str[0]) + pfree(str[0]); + + if (str[1]) + pfree(str[1]); + + return result; +} + + +/* + * + * PROCEDURE DBMS_ALERT.WAITONE(name IN VARCHAR2, message OUT VARCHAR2 + * ,status OUT INTEGER + * ,timeout IN NUMBER DEFAULT MAXWAIT); + * + * Waits for up to timeout seconds for notification of alert name. If status = 0 + * then message contains alert information. If status = 1 then timeout + * seconds elapsed without notification. + * + */ + +Datum +dbms_alert_waitone(PG_FUNCTION_ARGS) +{ + text *name; + float8 timeout; + TupleDesc tupdesc; + AttInMetadata *attinmeta; + HeapTuple tuple; + Datum result; + int message_id; + char *str[2] = {NULL,"1"}; + char *event_name; + int cycle = 0; + float8 endtime; + TupleDesc btupdesc; + + if (PG_ARGISNULL(0)) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("event name is NULL"), + errdetail("Eventname may not be NULL."))); + + if (PG_ARGISNULL(1)) + timeout = TDAYS; + else + timeout = PG_GETARG_FLOAT8(1); + + name = PG_GETARG_TEXT_P(0); + + WATCH_PRE(timeout, endtime, cycle); + if (ora_lock_shmem(SHMEMMSGSZ, MAX_PIPES, MAX_EVENTS, MAX_LOCKS, false)) + { + if (NULL != find_event(name, false, &message_id)) + { + str[0] = find_and_remove_message_item(message_id, sid, + false, false, false, NULL, &event_name); + if (event_name != NULL) + { + str[1] = "0"; + pfree(event_name); + LWLockRelease(shmem_lockid); + break; + } + } + LWLockRelease(shmem_lockid); + } + WATCH_POST(timeout, endtime, cycle); + + get_call_result_type(fcinfo, NULL, &tupdesc); + btupdesc = BlessTupleDesc(tupdesc); + attinmeta = TupleDescGetAttInMetadata(btupdesc); + tuple = BuildTupleFromCStrings(attinmeta, str); + result = HeapTupleGetDatum(tuple); + + if (str[0]) + pfree(str[0]); + + return result; +} + + +/* + * + * PROCEDURE DBMS_ALERT.SET_DEFAULTS(sensitivity IN NUMBER); + * + * The SET_DEFAULTS procedure is used to set session configurable settings + * used by the DBMS_ALERT package. Currently, the polling loop interval sleep time + * is the only session setting that can be modified using this procedure. The + * header for this procedure is, + * + */ + +Datum +dbms_alert_set_defaults(PG_FUNCTION_ARGS) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("feature not supported"), + errdetail("Sensitivity isn't supported."))); + + PG_RETURN_VOID(); +} + + + +/* + * This code was originally in plpgsql + * + */ + +/* +CREATE OR REPLACE FUNCTION dbms_alert._defered_signal() RETURNS trigger AS $$ +BEGIN + PERFORM dbms_alert._signal(NEW.event, NEW.message); + DELETE FROM ora_alerts WHERE oid=NEW.oid; + RETURN NEW; +END; +$$ LANGUAGE plpgsql SECURITY DEFINER VOLATILE; +*/ + +#define DatumGetItemPointer(X) ((ItemPointer) DatumGetPointer(X)) +#define ItemPointerGetDatum(X) PointerGetDatum(X) + +Datum +dbms_alert_defered_signal(PG_FUNCTION_ARGS) +{ + TriggerData *trigdata = (TriggerData *) fcinfo->context; + TupleDesc tupdesc; + HeapTuple rettuple; + char *relname; + text *name; + text *message; + int event_col; + int message_col; + + Datum datum; + bool isnull; + int cycle = 0; + float8 endtime; + float8 timeout = 2; + + if (!CALLED_AS_TRIGGER(fcinfo)) + ereport(ERROR, + (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), + errmsg("not called by trigger manager"))); + + if (!TRIGGER_FIRED_BY_INSERT(trigdata->tg_event)) + ereport(ERROR, + (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), + errmsg("not called on valid event"))); + + if (SPI_connect() < 0) + ereport(ERROR, + (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), + errmsg("SPI_connect failed"))); + + if (strcmp((relname = SPI_getrelname(trigdata->tg_relation)), "ora_alerts") != 0) + ereport(ERROR, + (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), + errmsg("not called with valid relation"))); + + rettuple = trigdata->tg_trigtuple; + tupdesc = trigdata->tg_relation->rd_att; + + if (SPI_ERROR_NOATTRIBUTE == (event_col = SPI_fnumber(tupdesc, "event"))) + ereport(ERROR, + (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), + errmsg("attribute event not found"))); + + + if (SPI_ERROR_NOATTRIBUTE == (message_col = SPI_fnumber(tupdesc, "message"))) + ereport(ERROR, + (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), + errmsg("attribute message not found"))); + + datum = SPI_getbinval(rettuple, tupdesc, event_col, &isnull); + if (isnull) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("event name is NULL"), + errdetail("Eventname may not be NULL."))); + name = DatumGetTextP(datum); + + datum = SPI_getbinval(rettuple, tupdesc, message_col, &isnull); + if (isnull) + message = NULL; + else + message = DatumGetTextP(datum); + + WATCH_PRE(timeout, endtime, cycle); + if (ora_lock_shmem(SHMEMMSGSZ, MAX_PIPES, MAX_EVENTS, MAX_LOCKS, false)) + { + ItemPointer tid; + Oid argtypes[1] = {TIDOID}; + char nulls[1] = {' '}; + Datum values[1]; + void *plan; + + create_message(name, message); + LWLockRelease(shmem_lockid); + + tid = &rettuple->t_data->t_ctid; + + if (!(plan = SPI_prepare("DELETE FROM ora_alerts WHERE ctid = $1", 1, argtypes))) + ereport(ERROR, + (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), + errmsg("SPI_prepare failed"))); + + values[0] = ItemPointerGetDatum(tid); + + if (SPI_OK_DELETE != SPI_execute_plan(plan, values, nulls, false, 1)) + ereport(ERROR, + (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), + errmsg("can't execute sql"))); + + SPI_finish(); + return PointerGetDatum(rettuple); + } + WATCH_POST(timeout, endtime, cycle); + LOCK_ERROR(); + + PG_RETURN_NULL(); +} + + +/* + * + * PROCEDURE DBMS_ALERT.SIGNAL(name IN VARCHAR2,message IN VARCHAR2); + * + * Signals the occurrence of alert name and attaches message. (Sessions + * registered for alert name are notified only when the signaling transaction + * commits.) + * + */ + +/* + +CREATE OR REPLACE FUNCTION dbms_alert.signal(_event text, _message text) RETURNS void AS $$ +BEGIN + PERFORM 1 FROM pg_catalog.pg_class c + WHERE pg_catalog.pg_table_is_visible(c.oid) + AND c.relkind='r' AND c.relname = 'ora_alerts'; + IF NOT FOUND THEN + CREATE TEMP TABLE ora_alerts(event text, message text) WITH OIDS; + REVOKE ALL ON TABLE ora_alerts FROM PUBLIC; + CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON ora_alerts + INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE dbms_alert._defered_signal(); + END IF; + INSERT INTO ora_alerts(event, message) VALUES(_event, _message); +END; +$$ LANGUAGE plpgsql VOLATILE SECURITY DEFINER; +*/ + +#define SPI_EXEC(cmd,_type_) \ +if (SPI_OK_##_type_ != SPI_exec(cmd, 1)) \ + ereport(ERROR, \ + (errcode(ERRCODE_INTERNAL_ERROR), \ + errmsg("SPI execute error"), \ + errdetail("Can't execute %s.", cmd))); + +Datum +dbms_alert_signal(PG_FUNCTION_ARGS) +{ + void *plan; + Oid argtypes[] = {TEXTOID, TEXTOID}; + Datum values[2]; + char nulls[2] = {' ',' '}; + + if (PG_ARGISNULL(0)) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("event name is NULL"), + errdetail("Eventname may not be NULL."))); + + if (PG_ARGISNULL(1)) + nulls[1] = 'n'; + + values[0] = PG_GETARG_DATUM(0); + values[1] = PG_GETARG_DATUM(1); + + if (SPI_connect() < 0) + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("SPI_connect failed"))); + + SPI_EXEC("SELECT 1 FROM pg_catalog.pg_class c " + "WHERE pg_catalog.pg_table_is_visible(c.oid) " + "AND c.relkind='r' AND c.relname = 'ora_alerts'", SELECT); + if (0 == SPI_processed) + { + SPI_EXEC("CREATE TEMP TABLE ora_alerts(event text, message text)", UTILITY); + SPI_EXEC("REVOKE ALL ON TABLE ora_alerts FROM PUBLIC", UTILITY); + SPI_EXEC("CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON ora_alerts " + "INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE dbms_alert.defered_signal()", UTILITY); + } + + if (!(plan = SPI_prepare( + "INSERT INTO ora_alerts(event,message) VALUES($1, $2)", + 2, + argtypes))) + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("SPI_prepare failed"))); + + if (SPI_OK_INSERT != SPI_execute_plan(plan, values, nulls, false, 1)) + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("can't execute sql"))); + + SPI_finish(); + PG_RETURN_VOID(); +} diff --git a/src/postgres/third-party-extensions/orafce/assert.c b/src/postgres/third-party-extensions/orafce/assert.c new file mode 100644 index 000000000000..f011437fad4f --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/assert.c @@ -0,0 +1,404 @@ +#include "postgres.h" +#include "funcapi.h" +#include "assert.h" +#include "miscadmin.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/syscache.h" +#include "catalog/namespace.h" + +#if PG_VERSION_NUM >= 120000 + +#include "catalog/pg_namespace_d.h" + +#endif + +#include "ctype.h" +#include "string.h" +#include "orafce.h" +#include "builtins.h" + +#if PG_VERSION_NUM >= 100000 + +#include "utils/regproc.h" + +#endif + + +PG_FUNCTION_INFO_V1(dbms_assert_enquote_literal); +PG_FUNCTION_INFO_V1(dbms_assert_enquote_name); +PG_FUNCTION_INFO_V1(dbms_assert_noop); +PG_FUNCTION_INFO_V1(dbms_assert_qualified_sql_name); +PG_FUNCTION_INFO_V1(dbms_assert_schema_name); +PG_FUNCTION_INFO_V1(dbms_assert_simple_sql_name); +PG_FUNCTION_INFO_V1(dbms_assert_object_name); + + +#define CUSTOM_EXCEPTION(code, msg) \ + ereport(ERROR, \ + (errcode(ERRCODE_ORA_PACKAGES_##code), \ + errmsg(msg))) + +#define INVALID_SCHEMA_NAME_EXCEPTION() \ + CUSTOM_EXCEPTION(INVALID_SCHEMA_NAME, "invalid schema name") + +#define INVALID_OBJECT_NAME_EXCEPTION() \ + CUSTOM_EXCEPTION(INVALID_OBJECT_NAME, "invalid object name") + +#define ISNOT_SIMPLE_SQL_NAME_EXCEPTION() \ + CUSTOM_EXCEPTION(ISNOT_SIMPLE_SQL_NAME, "string is not simple SQL name") + +#define ISNOT_QUALIFIED_SQL_NAME_EXCEPTION() \ + CUSTOM_EXCEPTION(ISNOT_QUALIFIED_SQL_NAME, "string is not qualified SQL name") + +#define EMPTY_STR(str) ((VARSIZE(str) - VARHDRSZ) == 0) + +static bool check_sql_name(char *cp, int len); +static bool ParseIdentifierString(char *rawstring); + +/* + * Procedure ParseIdentifierString is based on SplitIdentifierString + * from varlena.c. We need different behave of quote symbol evaluation. + */ +bool +ParseIdentifierString(char *rawstring) +{ + char *nextp = rawstring; + bool done = false; + + while (isspace((unsigned char) *nextp)) + nextp++; /* skip leading whitespace */ + + if (*nextp == '\0') + return true; /* allow empty string */ + + /* At the top of the loop, we are at start of a new identifier. */ + do + { + char *curname; + char *endp; + + if (*nextp == '\"') + { + /* Quoted name --- collapse quote-quote pairs, no downcasing */ + curname = nextp + 1; + for (;;) + { + endp = strchr(nextp + 1, '\"'); + if (endp == NULL) + return false; /* mismatched quotes */ + if (endp[1] != '\"') + break; /* found end of quoted name */ + /* Collapse adjacent quotes into one quote, and look again */ + memmove(endp, endp + 1, strlen(endp)); + nextp = endp; + } + /* endp now points at the terminating quote */ + nextp = endp + 1; + } + else + { + /* Unquoted name --- extends to separator or whitespace */ + curname = nextp; + while (*nextp && *nextp != '.' && + !isspace((unsigned char) *nextp)) + { + if (!isalnum(*nextp) && *nextp != '_') + return false; + nextp++; + } + endp = nextp; + if (curname == nextp) + return false; /* empty unquoted name not allowed */ + } + + while (isspace((unsigned char) *nextp)) + nextp++; /* skip trailing whitespace */ + + if (*nextp == '.') + { + nextp++; + while (isspace((unsigned char) *nextp)) + nextp++; /* skip leading whitespace for next */ + /* we expect another name, so done remains false */ + } + else if (*nextp == '\0') + done = true; + else + return false; /* invalid syntax */ + + /* Loop back if we didn't reach end of string */ + } while (!done); + + return true; +} + + + +/**************************************************************** + * DBMS_ASSERT.ENQUOTE_LITERAL + * + * Syntax: + * FUNCTION ENQUOTE_LITERAL(str varchar) RETURNS varchar; + * + * Purpouse: + * Add leading and trailing quotes, verify that all single quotes + * are paired with adjacent single quotes. + * + ****************************************************************/ + +Datum +dbms_assert_enquote_literal(PG_FUNCTION_ARGS) +{ + PG_RETURN_DATUM(DirectFunctionCall1(quote_literal, PG_GETARG_DATUM(0))); +} + + +/**************************************************************** + * DBMS_ASSERT.ENQUOTE_NAME + * + * Syntax: + * FUNCTION ENQUOTE_NAME(str varchar) RETURNS varchar; + * FUNCTION ENQUOTE_NAME(str varchar, loweralize boolean := true) + * RETURNS varchar; + * Purpouse: + * Enclose name in double quotes. + * Atention!: + * On Oracle is second parameter capitalize! + * + ****************************************************************/ + +Datum +dbms_assert_enquote_name(PG_FUNCTION_ARGS) +{ + Datum name = PG_GETARG_DATUM(0); + bool loweralize = PG_GETARG_BOOL(1); + Oid collation = PG_GET_COLLATION(); + + name = DirectFunctionCall1(quote_ident, name); + + if (loweralize) + name = DirectFunctionCall1Coll(lower, collation, name); + + PG_RETURN_DATUM(name); +} + + +/**************************************************************** + * DBMS_ASSERT.NOOP + * + * Syntax: + * FUNCTION NOOP(str varchar) RETURNS varchar; + * + * Purpouse: + * Returns value without any checking. + * + ****************************************************************/ + +Datum +dbms_assert_noop(PG_FUNCTION_ARGS) +{ + text *str = PG_GETARG_TEXT_P(0); + + PG_RETURN_TEXT_P(TextPCopy(str)); +} + + +/**************************************************************** + * DBMS_ASSERT.QUALIFIED_SQL_NAME + * + * Syntax: + * FUNCTION QUALIFIED_SQL_NAME(str varchar) RETURNS varchar; + * + * Purpouse: + * This function verifies that the input string is qualified SQL + * name. + * Exception: 44004 string is not a qualified SQL name + * + ****************************************************************/ + +Datum +dbms_assert_qualified_sql_name(PG_FUNCTION_ARGS) +{ + text *qname; + + if (PG_ARGISNULL(0)) + ISNOT_QUALIFIED_SQL_NAME_EXCEPTION(); + + qname = PG_GETARG_TEXT_P(0); + if (EMPTY_STR(qname)) + ISNOT_QUALIFIED_SQL_NAME_EXCEPTION(); + + if (!ParseIdentifierString(text_to_cstring(qname))) + ISNOT_QUALIFIED_SQL_NAME_EXCEPTION(); + + PG_RETURN_TEXT_P(qname); +} + + +/**************************************************************** + * DBMS_ASSERT.SCHEMA_NAME + * + * Syntax: + * FUNCTION SCHEMA_NAME(str varchar) RETURNS varchar; + * + * Purpouse: + * Function verifies that input string is an existing schema + * name. + * Exception: 44001 Invalid schema name + * + ****************************************************************/ + +Datum +dbms_assert_schema_name(PG_FUNCTION_ARGS) +{ + Oid namespaceId; + AclResult aclresult; + text *sname; + char *nspname; + List *names; + + if (PG_ARGISNULL(0)) + INVALID_SCHEMA_NAME_EXCEPTION(); + + sname = PG_GETARG_TEXT_P(0); + if (EMPTY_STR(sname)) + INVALID_SCHEMA_NAME_EXCEPTION(); + + nspname = text_to_cstring(sname); + names = stringToQualifiedNameList(nspname); + if (list_length(names) != 1) + INVALID_SCHEMA_NAME_EXCEPTION(); + +#if PG_VERSION_NUM >= 120000 + + namespaceId = GetSysCacheOid(NAMESPACENAME, Anum_pg_namespace_oid, + CStringGetDatum(strVal(linitial(names))), + 0, 0, 0); + +#else + + namespaceId = GetSysCacheOid(NAMESPACENAME, + CStringGetDatum(strVal(linitial(names))), + 0, 0, 0); + +#endif + + if (!OidIsValid(namespaceId)) + INVALID_SCHEMA_NAME_EXCEPTION(); + + aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_USAGE); + if (aclresult != ACLCHECK_OK) + INVALID_SCHEMA_NAME_EXCEPTION(); + + PG_RETURN_TEXT_P(sname); +} + + +/**************************************************************** + * DBMS_ASSERT.SIMPLE_SQL_NAME + * + * Syntax: + * FUNCTION SIMPLE_SQL_NAME(str varchar) RETURNS varchar; + * + * Purpouse: + * This function verifies that the input string is simple SQL + * name. + * Exception: 44003 String is not a simple SQL name + * + ****************************************************************/ + +static bool +check_sql_name(char *cp, int len) +{ + if (*cp == '"') + { + for (cp++, len -= 2; len-- > 0; cp++) + { + /* all double quotes have to be paired */ + if (*cp == '"') + { + if (len-- == 0) + return false; + /* next char has to be quote */ + if (*cp != '"') + return false; + } + + } + if (*cp != '"') + return false; + } + else + { + /* Doesn't allow national characters in sql name :( */ + for (; len-- > 0; cp++) + if (!isalnum(*cp) && *cp != '_') + return false; + } + + return true; +} + +Datum +dbms_assert_simple_sql_name(PG_FUNCTION_ARGS) +{ + text *sname; + int len; + char *cp; + + if (PG_ARGISNULL(0)) + ISNOT_SIMPLE_SQL_NAME_EXCEPTION(); + + sname = PG_GETARG_TEXT_P(0); + if (EMPTY_STR(sname)) + ISNOT_SIMPLE_SQL_NAME_EXCEPTION(); + + len = VARSIZE(sname) - VARHDRSZ; + cp = VARDATA(sname); + + if (!check_sql_name(cp, len)) + ISNOT_SIMPLE_SQL_NAME_EXCEPTION(); + + PG_RETURN_TEXT_P(sname); +} + + +/**************************************************************** + * DBMS_ASSERT.OBJECT_NAME + * + * Syntax: + * FUNCTION OBJECT_NAME(str varchar) RETURNS varchar; + * + * Purpouse: + * Verifies that input string is qualified SQL identifier of + * an existing SQL object. + * Exception: 44002 Invalid object name + * + ****************************************************************/ + +Datum +dbms_assert_object_name(PG_FUNCTION_ARGS) +{ + List *names; + text *str; + char *object_name; + Oid classId; + + if (PG_ARGISNULL(0)) + INVALID_OBJECT_NAME_EXCEPTION(); + + str = PG_GETARG_TEXT_P(0); + if (EMPTY_STR(str)) + INVALID_OBJECT_NAME_EXCEPTION(); + + object_name = text_to_cstring(str); + + names = stringToQualifiedNameList(object_name); + + classId = RangeVarGetRelid(makeRangeVarFromNameList(names), NoLock, true); + if (!OidIsValid(classId)) + INVALID_OBJECT_NAME_EXCEPTION(); + + PG_RETURN_TEXT_P(str); +} diff --git a/src/postgres/third-party-extensions/orafce/assert.h b/src/postgres/third-party-extensions/orafce/assert.h new file mode 100644 index 000000000000..45c2fc7f1d21 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/assert.h @@ -0,0 +1,9 @@ +#ifndef __ASSERT__ +#define __ASSERT__ + +#define ERRCODE_ORA_PACKAGES_INVALID_SCHEMA_NAME MAKE_SQLSTATE('4','4','0','0','1') +#define ERRCODE_ORA_PACKAGES_INVALID_OBJECT_NAME MAKE_SQLSTATE('4','4','0','0','2') +#define ERRCODE_ORA_PACKAGES_ISNOT_SIMPLE_SQL_NAME MAKE_SQLSTATE('4','4','0','0','3') +#define ERRCODE_ORA_PACKAGES_ISNOT_QUALIFIED_SQL_NAME MAKE_SQLSTATE('4','4','0','0','4') + +#endif diff --git a/src/postgres/third-party-extensions/orafce/builtins.h b/src/postgres/third-party-extensions/orafce/builtins.h new file mode 100644 index 000000000000..d5581fd5a3f0 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/builtins.h @@ -0,0 +1,294 @@ + +#ifndef ORAFCE_BUILTINS +#define ORAFCE_BUILTINS + +#ifndef PGDLLEXPORT +#ifdef _MSC_VER +#define PGDLLEXPORT __declspec(dllexport) + +/* + * PG_MODULE_MAGIC and PG_FUNCTION_INFO_V1 macros are broken for MSVC. + * So, we redefine them. + */ + +#undef PG_MODULE_MAGIC +#define PG_MODULE_MAGIC \ +extern PGDLLEXPORT const Pg_magic_struct *PG_MAGIC_FUNCTION_NAME(void); \ +const Pg_magic_struct * \ +PG_MAGIC_FUNCTION_NAME(void) \ +{ \ + static const Pg_magic_struct Pg_magic_data = PG_MODULE_MAGIC_DATA; \ + return &Pg_magic_data; \ +} \ +extern int no_such_variable + +#undef PG_FUNCTION_INFO_V1 +#define PG_FUNCTION_INFO_V1(funcname) \ +extern PGDLLEXPORT const Pg_finfo_record * CppConcat(pg_finfo_,funcname)(void); \ +const Pg_finfo_record * \ +CppConcat(pg_finfo_,funcname) (void) \ +{ \ + static const Pg_finfo_record my_finfo = { 1 }; \ + return &my_finfo; \ +} \ +extern int no_such_variable + +#else +#define PGDLLEXPORT PGDLLIMPORT +#endif +#endif + +/* from aggregate.c */ +extern PGDLLEXPORT Datum orafce_listagg1_transfn(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum orafce_wm_concat_transfn(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum orafce_listagg2_transfn(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum orafce_listagg_finalfn(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum orafce_median4_transfn(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum orafce_median4_finalfn(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum orafce_median8_transfn(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum orafce_median8_finalfn(PG_FUNCTION_ARGS); + +/* from alert.c */ +extern PGDLLEXPORT Datum dbms_alert_register(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_alert_remove(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_alert_removeall(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_alert_set_defaults(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_alert_signal(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_alert_waitany(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_alert_waitone(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_alert_defered_signal(PG_FUNCTION_ARGS); + +/* from assert.c */ +extern PGDLLEXPORT Datum dbms_assert_enquote_literal(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_assert_enquote_name(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_assert_noop(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_assert_qualified_sql_name(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_assert_schema_name(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_assert_simple_sql_name(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_assert_object_name(PG_FUNCTION_ARGS); + +/* from convert.c */ +extern PGDLLEXPORT Datum orafce_to_char_int4(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum orafce_to_char_int8(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum orafce_to_char_float4(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum orafce_to_char_float8(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum orafce_to_char_numeric(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum orafce_to_char_timestamp(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum orafce_to_number(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum orafce_to_multi_byte(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum orafce_to_single_byte(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum orafce_unistr(PG_FUNCTION_ARGS); + +/* from datefce.c */ +extern PGDLLEXPORT Datum next_day(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum next_day_by_index(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum last_day(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum months_between(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum add_months(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum ora_to_date(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum ora_date_trunc(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum ora_date_round(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum ora_timestamptz_trunc(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum ora_timestamptz_round(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum ora_timestamp_trunc(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum ora_timestamp_round(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum orafce_sysdate(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum orafce_sessiontimezone(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum orafce_dbtimezone(PG_FUNCTION_ARGS); + +/* from file.c */ +extern PGDLLEXPORT Datum utl_file_fopen(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum utl_file_is_open(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum utl_file_get_line(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum utl_file_get_nextline(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum utl_file_put(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum utl_file_put_line(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum utl_file_new_line(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum utl_file_putf(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum utl_file_fflush(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum utl_file_fclose(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum utl_file_fclose_all(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum utl_file_fremove(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum utl_file_frename(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum utl_file_fcopy(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum utl_file_fgetattr(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum utl_file_tmpdir(PG_FUNCTION_ARGS); + +/* from others.c */ +extern PGDLLEXPORT Datum ora_nvl(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum ora_nvl2(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum ora_concat(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum ora_nlssort(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum ora_set_nls_sort(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum ora_lnnvl(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum ora_decode(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum orafce_dump(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum ora_get_major_version(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum ora_get_major_version_num(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum ora_get_full_version_num(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum ora_get_platform(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum ora_get_status(PG_FUNCTION_ARGS); + +/* from pipe.c */ +extern PGDLLEXPORT Datum dbms_pipe_pack_message_text(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_pipe_unpack_message_text(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_pipe_send_message(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_pipe_receive_message(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_pipe_unique_session_name(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_pipe_list_pipes(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_pipe_next_item_type(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_pipe_create_pipe(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_pipe_create_pipe_2(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_pipe_create_pipe_1(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_pipe_reset_buffer(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_pipe_purge(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_pipe_remove_pipe(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_pipe_pack_message_date(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_pipe_unpack_message_date(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_pipe_pack_message_timestamp(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_pipe_unpack_message_timestamp(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_pipe_pack_message_number(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_pipe_unpack_message_number(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_pipe_pack_message_bytea(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_pipe_unpack_message_bytea(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_pipe_pack_message_record(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_pipe_unpack_message_record(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_pipe_pack_message_integer(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_pipe_pack_message_bigint(PG_FUNCTION_ARGS); + +/* from plunit.c */ +extern PGDLLEXPORT Datum plunit_assert_true(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plunit_assert_true_message(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plunit_assert_false(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plunit_assert_false_message(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plunit_assert_null(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plunit_assert_null_message(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plunit_assert_not_null(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plunit_assert_not_null_message(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plunit_assert_equals(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plunit_assert_equals_message(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plunit_assert_equals_range(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plunit_assert_equals_range_message(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plunit_assert_not_equals(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plunit_assert_not_equals_message(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plunit_assert_not_equals_range(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plunit_assert_not_equals_range_message(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plunit_fail(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plunit_fail_message(PG_FUNCTION_ARGS); + +/* from plvdate.c */ +extern PGDLLEXPORT Datum plvdate_add_bizdays(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvdate_nearest_bizday(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvdate_next_bizday(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvdate_bizdays_between(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvdate_prev_bizday(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvdate_isbizday(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvdate_set_nonbizday_dow(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvdate_unset_nonbizday_dow(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvdate_set_nonbizday_day(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvdate_unset_nonbizday_day(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvdate_use_easter(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvdate_using_easter(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvdate_use_great_friday(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvdate_using_great_friday(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvdate_include_start(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvdate_including_start(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvdate_default_holidays(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvdate_version(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvdate_days_inmonth(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvdate_isleapyear(PG_FUNCTION_ARGS); + +/* from plvlec.c */ +extern PGDLLEXPORT Datum plvlex_tokens(PG_FUNCTION_ARGS); + +/* from plvstr.c */ +extern PGDLLEXPORT Datum plvstr_rvrs(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvstr_normalize(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvstr_is_prefix_text(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvstr_is_prefix_int(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvstr_is_prefix_int64(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvstr_lpart(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvstr_rpart(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvstr_lstrip(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvstr_rstrip(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvstr_left(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvstr_right(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvstr_substr2(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvstr_substr3(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvstr_instr2(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvstr_instr3(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvstr_instr4(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvstr_betwn_i(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvstr_betwn_c(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvstr_swap(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvchr_nth(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvchr_first(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvchr_last(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvchr_is_kind_i(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvchr_is_kind_a(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvchr_char_name(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum oracle_substr2(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum oracle_substr3(PG_FUNCTION_ARGS); + +/* from plvsubst.c */ +extern PGDLLEXPORT Datum plvsubst_string_array(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvsubst_string_string(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvsubst_setsubst(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvsubst_setsubst_default(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum plvsubst_subst(PG_FUNCTION_ARGS); + +/* from putline.c */ +extern PGDLLEXPORT Datum dbms_output_enable(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_output_enable_default(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_output_disable(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_output_serveroutput(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_output_put(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_output_put_line(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_output_new_line(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_output_get_line(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_output_get_lines(PG_FUNCTION_ARGS); + +/* from random.c */ +extern PGDLLEXPORT Datum dbms_random_initialize(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_random_normal(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_random_random(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_random_seed_int(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_random_seed_varchar(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_random_string(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_random_terminate(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_random_value(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_random_value_range(PG_FUNCTION_ARGS); + +/* from utility.c */ +extern PGDLLEXPORT Datum dbms_utility_format_call_stack0(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum dbms_utility_format_call_stack1(PG_FUNCTION_ARGS); + +/* from oraguc.c */ +extern void PGDLLEXPORT _PG_init(void); + +/* from charpad.c */ +extern PGDLLEXPORT Datum orafce_lpad(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum orafce_rpad(PG_FUNCTION_ARGS); + +/* from charlen.c */ +extern PGDLLEXPORT Datum orafce_bpcharlen(PG_FUNCTION_ARGS); + +/* from varchar2.c */ +extern PGDLLEXPORT Datum varchar2in(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum varchar2out(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum varchar2(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum varchar2recv(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum orafce_concat2(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum orafce_varchar_transform(PG_FUNCTION_ARGS); + +/* from nvarchar2.c */ +extern PGDLLEXPORT Datum nvarchar2in(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum nvarchar2out(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum nvarchar2(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum nvarchar2recv(PG_FUNCTION_ARGS); + +/* from replace_empty_string.c */ +extern PGDLLEXPORT Datum orafce_replace_empty_strings(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum orafce_replace_null_strings(PG_FUNCTION_ARGS); + +#endif diff --git a/src/postgres/third-party-extensions/orafce/charlen.c b/src/postgres/third-party-extensions/orafce/charlen.c new file mode 100644 index 000000000000..3e71afae5b3e --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/charlen.c @@ -0,0 +1,39 @@ +/* + * charlen.c + * + * provides a modified version of bpcharlen() that does not + * ignore trailing spaces of CHAR arguments to provide an + * Oracle compatible length() function + */ + +#include "postgres.h" + +#include "utils/builtins.h" +#include "access/hash.h" +#include "libpq/pqformat.h" +#include "nodes/nodeFuncs.h" +#include "utils/array.h" +#include "utils/formatting.h" +#include "mb/pg_wchar.h" +#include "fmgr.h" + +#include "orafce.h" +#include "builtins.h" + +PG_FUNCTION_INFO_V1(orafce_bpcharlen); + +Datum +orafce_bpcharlen(PG_FUNCTION_ARGS) +{ + BpChar *arg = PG_GETARG_BPCHAR_PP(0); + int len; + + /* byte-length of the argument (trailing spaces not ignored) */ + len = VARSIZE_ANY_EXHDR(arg); + + /* in multibyte encoding, convert to number of characters */ + if (pg_database_encoding_max_length() != 1) + len = pg_mbstrlen_with_len(VARDATA_ANY(arg), len); + + PG_RETURN_INT32(len); +} diff --git a/src/postgres/third-party-extensions/orafce/charpad.c b/src/postgres/third-party-extensions/orafce/charpad.c new file mode 100644 index 000000000000..5ea86a04e52b --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/charpad.c @@ -0,0 +1,486 @@ +/*---------------------------------------------------------------------------- + * + * charpad.c + * LPAD and RPAD SQL functions for PostgreSQL. + * + *---------------------------------------------------------------------------- + */ + +#include "postgres.h" +#include "utils/builtins.h" +#include "utils/formatting.h" +#include "mb/pg_wchar.h" +#include "fmgr.h" + +#include "orafce.h" +#include "builtins.h" + +/* flags */ +#define ON true +#define OFF false + +/* Upper limit on total width of the padded output of *pad functions */ +#define PAD_MAX 4000 + +PG_FUNCTION_INFO_V1(orafce_lpad); +PG_FUNCTION_INFO_V1(orafce_rpad); + +/* + * orafce_lpad(string text, length int32 [, fill text]) + * + * Fill up the string to length 'length' by prepending + * the characters fill (a half-width space by default) + */ +Datum +orafce_lpad(PG_FUNCTION_ARGS) +{ + text *string1 = PG_GETARG_TEXT_PP(0); + int32 output_width = PG_GETARG_INT32(1); + text *string2 = PG_GETARG_TEXT_PP(2); + text *ret; + char *ptr1, + *ptr2 = NULL, + *ptr2start = NULL, + *ptr2end = NULL, + *ptr_ret, + *spc = " "; + int mlen, + dsplen, + s1blen, + s2blen, + hslen, + total_blen = 0, + s1_width = 0, + s2_add_width = 0, + s1_add_blen = 0, + s2_add_blen = 0; + bool s2_operate = ON, + half_space = OFF, + init_ptr = ON; + + /* validate output width (the 2nd argument) */ + if (output_width < 0) + output_width = 0; + if (output_width > PAD_MAX) + output_width = PAD_MAX; + + /* get byte-length of the 1st and 3rd argument strings */ + s1blen = VARSIZE_ANY_EXHDR(string1); + s2blen = VARSIZE_ANY_EXHDR(string2); + + /* validate the lengths */ + if (s1blen < 0) + s1blen = 0; + if (s2blen < 0) + s2blen = 0; + + /* if the filler length is zero disable filling */ + if (s2blen == 0) + { + s2_operate = OFF; /* turn off string2 processing flag */ + output_width = 0; /* same behavior as Oracle database */ + } + + /* byte-length of half-width space */ + hslen = pg_mblen(spc); + + /* + * Calculate the length of the portion of string1 to include in + * the final output + */ + ptr1 = VARDATA_ANY(string1); + while (s1blen > 0) + { + /* byte-length and display length per character of string1 */ + mlen = pg_mblen(ptr1); + dsplen = pg_dsplen(ptr1); + + /* accumulate display length of string1 */ + s1_width += dsplen; + + /* + * if string1 is longer/wider than the requested output_width, + * discard this character and prepend a half-width space instead + */ + if(s1_width >= output_width) + { + if(s1_width != output_width) + { + /* secure bytes for a half-width space in the final output */ + if (output_width != 0) + { + s1_add_blen += hslen; + half_space = ON; + } + } + else /* exactly fits, so include this chracter */ + { + s1_add_blen += mlen; + } + + /* + * turn off string2 processing because string1 already + * consumed output_width + */ + s2_operate = OFF; + + /* done with string1 */ + break; + } + + /* accumulate string1's portion of byte-length of the output */ + s1_add_blen += mlen; + + /* advance one character within string1 */ + ptr1 += mlen; + + /* loop counter */ + s1blen -= mlen; + } + + /* Calculate the length of the portion composed of string2 to use for padding */ + if (s2_operate) + { + /* remaining part of output_width is composed of string2 */ + s2_add_width = output_width - s1_width; + + ptr2 = ptr2start = VARDATA_ANY(string2); + ptr2end = ptr2 + s2blen; + + while (s2_add_width > 0) + { + /* byte-length and display length per character of string2 */ + mlen = pg_mblen(ptr2); + dsplen = pg_dsplen(ptr2); + + /* + * output_width can not fit this character of string2, so discard it and + * prepend a half-width space instead + */ + if(dsplen > s2_add_width) + { + s2_add_blen += hslen; + half_space = ON; + + /* done with string2 */ + break; + } + + /* accumulate string2's portion of byte-length of the output */ + s2_add_blen += mlen; + + /* loop counter */ + s2_add_width -= dsplen; + + /* advance one character within string2 */ + ptr2 += mlen; + + /* when get to the end of string2, reset ptr2 to the start */ + if (ptr2 == ptr2end) + ptr2 = ptr2start; + } + } + + /* allocate enough space to contain output_width worth of characters */ + total_blen = s1_add_blen + s2_add_blen; + ret = (text *) palloc(VARHDRSZ + total_blen); + ptr_ret = VARDATA(ret); + + /* + * add a half-width space as a padding necessary to satisfy the required + * output_width + * + * (memory already allocated as reserved by either s1_add_blen + * or s2_add_blen) + */ + if (half_space) + { + memcpy(ptr_ret, spc, hslen); + ptr_ret += hslen; + } + + /* prepend string2 padding */ + while(s2_add_blen > 0) + { + /* reset ptr2 to the string2 start */ + if(init_ptr) + { + init_ptr = OFF; + ptr2 = ptr2start; + } + + mlen = pg_mblen(ptr2); + if ( s2_add_blen < mlen ) + break; + + memcpy(ptr_ret, ptr2, mlen); + ptr_ret += mlen; + ptr2 += mlen; + + /* loop counter */ + s2_add_blen -= mlen; + + /* when get to the end of string2, reset ptr2 back to the start */ + if (ptr2 == ptr2end) + ptr2 = ptr2start; + } + + init_ptr = ON; + + /* string1 */ + while(s1_add_blen > 0) + { + /* reset ptr1 back to the start of string1 */ + if(init_ptr) + { + init_ptr = OFF; + ptr1 = VARDATA_ANY(string1); + } + + mlen = pg_mblen(ptr1); + + if( s1_add_blen < mlen ) + break; + + memcpy(ptr_ret, ptr1, mlen); + ptr_ret += mlen; + ptr1 += mlen; + + /* loop counter */ + s1_add_blen -= mlen; + } + + SET_VARSIZE(ret, ptr_ret - (char *) ret); + + PG_RETURN_TEXT_P(ret); +} + +/* + * orafce_rpad(string text, length int32 [, fill text]) + * + * Fill up the string to length 'length' by appending + * the characters fill (a half-width space by default) + */ +Datum +orafce_rpad(PG_FUNCTION_ARGS) +{ + text *string1 = PG_GETARG_TEXT_PP(0); + int32 output_width = PG_GETARG_INT32(1); + text *string2 = PG_GETARG_TEXT_PP(2); + text *ret; + char *ptr1, + *ptr2 = NULL, + *ptr2start = NULL, + *ptr2end = NULL, + *ptr_ret, + *spc = " "; + int mlen, + dsplen, + s1blen, + s2blen, + hslen, + total_blen = 0, + s1_width = 0, + s2_add_width = 0, + s1_add_blen = 0, + s2_add_blen = 0; + bool s2_operate = ON, + half_space = OFF, + init_ptr = ON; + + /* validate output width (the 2nd argument) */ + if (output_width < 0) + output_width = 0; + if (output_width > PAD_MAX) + output_width = PAD_MAX; + + /* get byte-length of the 1st and 3rd argument strings */ + s1blen = VARSIZE_ANY_EXHDR(string1); + s2blen = VARSIZE_ANY_EXHDR(string2); + + /* validate the lengths */ + if (s1blen < 0) + s1blen = 0; + if (s2blen < 0) + s2blen = 0; + + /* if the filler length is zero disable filling */ + if (s2blen == 0) + { + s2_operate = OFF; /* turn off string2 processing flag */ + output_width = 0; /* same behavior as Oracle database */ + } + + /* byte-length of half-width space */ + hslen = pg_mblen(spc); + + /* + * Calculate the length of the portion of string1 to include in + * the final output + */ + ptr1 = VARDATA_ANY(string1); + while (s1blen > 0) + { + /* byte-length and display length per character of string1 */ + mlen = pg_mblen(ptr1); + dsplen = pg_dsplen(ptr1); + + /* accumulate display length of string1 */ + s1_width += dsplen; + + /* + * if string1 is longer/wider than the requested output_width, + * discard this character and prepend a half-width space instead + */ + if(s1_width >= output_width) + { + if(s1_width != output_width) + { + /* secure bytes for a half-width space in the final output */ + if (output_width != 0) + { + s1_add_blen += hslen; + half_space = ON; + } + } + else /* exactly fits, so include this chracter */ + { + s1_add_blen += mlen; + } + + /* + * turn off string2 processing because string1 already + * consumed output_width + */ + s2_operate = OFF; + + /* done with string1 */ + break; + } + + /* accumulate string1's portion of byte-length of the output */ + s1_add_blen += mlen; + + /* advance one character within string1 */ + ptr1 += mlen; + + /* loop counter */ + s1blen -= mlen; + } + + /* Calculate the length of the portion composed of string2 to use for padding */ + if (s2_operate) + { + /* remaining part of output_width is composed of string2 */ + s2_add_width = output_width - s1_width; + + ptr2 = ptr2start = VARDATA_ANY(string2); + ptr2end = ptr2 + s2blen; + + while (s2_add_width > 0) + { + /* byte-length and display length per character of string2 */ + mlen = pg_mblen(ptr2); + dsplen = pg_dsplen(ptr2); + + /* + * output_width can not fit this character of string2, so discard it and + * prepend a half-width space instead + */ + if(dsplen > s2_add_width) + { + s2_add_blen += hslen; + half_space = ON; + + /* done with string2 */ + break; + } + + /* accumulate string2's portion of byte-length of the output */ + s2_add_blen += mlen; + + /* loop counter */ + s2_add_width -= dsplen; + + /* advance one character within string2 */ + ptr2 += mlen; + + /* when get to the end of string2, reset ptr2 to the start */ + if (ptr2 == ptr2end) + ptr2 = ptr2start; + } + } + + /* allocate enough space to contain output_width worth of characters */ + total_blen = s1_add_blen + s2_add_blen; + ret = (text *) palloc(VARHDRSZ + total_blen); + ptr_ret = VARDATA(ret); + + /* string1 */ + while(s1_add_blen > 0) + { + /* reset ptr1 back to the start of string1 */ + if(init_ptr) + { + init_ptr = OFF; + ptr1 = VARDATA_ANY(string1); + } + + mlen = pg_mblen(ptr1); + + if( s1_add_blen < mlen ) + break; + + memcpy(ptr_ret, ptr1, mlen); + ptr_ret += mlen; + ptr1 += mlen; + + /* loop counter */ + s1_add_blen -= mlen; + } + + init_ptr = ON; + + /* append string2 padding */ + while(s2_add_blen > 0) + { + /* reset ptr2 to the string2 start */ + if(init_ptr) + { + init_ptr = OFF; + ptr2 = ptr2start; + } + + mlen = pg_mblen(ptr2); + if ( s2_add_blen < mlen ) + break; + + memcpy(ptr_ret, ptr2, mlen); + ptr_ret += mlen; + ptr2 += mlen; + + /* loop counter */ + s2_add_blen -= mlen; + + /* when get to the end of string2, reset ptr2 back to the start */ + if (ptr2 == ptr2end) + ptr2 = ptr2start; + } + + /* + * add a half-width space as a padding necessary to satisfy the required + * output_width + * + * (memory already allocated as reserved by either s1_add_blen + * or s2_add_blen) + */ + if (half_space) + { + memcpy(ptr_ret, spc, hslen); + ptr_ret += hslen; + } + + SET_VARSIZE(ret, ptr_ret - (char *) ret); + + PG_RETURN_TEXT_P(ret); +} diff --git a/src/postgres/third-party-extensions/orafce/convert.c b/src/postgres/third-party-extensions/orafce/convert.c new file mode 100644 index 000000000000..9534ac8c983e --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/convert.c @@ -0,0 +1,927 @@ +#include + +#include "postgres.h" +#include "fmgr.h" +#include "lib/stringinfo.h" +#include "mb/pg_wchar.h" +#include "utils/builtins.h" +#include "utils/numeric.h" +#include "utils/pg_locale.h" +#include "utils/formatting.h" + +#include "orafce.h" +#include "builtins.h" + +#if PG_VERSION_NUM < 130000 + +#include "catalog/namespace.h" +#include "utils/memutils.h" + +#endif + +PG_FUNCTION_INFO_V1(orafce_to_char_int4); +PG_FUNCTION_INFO_V1(orafce_to_char_int8); +PG_FUNCTION_INFO_V1(orafce_to_char_float4); +PG_FUNCTION_INFO_V1(orafce_to_char_float8); +PG_FUNCTION_INFO_V1(orafce_to_char_numeric); +PG_FUNCTION_INFO_V1(orafce_to_char_timestamp); +PG_FUNCTION_INFO_V1(orafce_to_number); +PG_FUNCTION_INFO_V1(orafce_to_multi_byte); +PG_FUNCTION_INFO_V1(orafce_to_single_byte); +PG_FUNCTION_INFO_V1(orafce_unistr); + +static int getindex(const char **map, char *mbchar, int mblen); + +#if PG_VERSION_NUM < 130000 + +static FmgrInfo *orafce_Utf8ToServerConvProc = NULL; + +#endif + +Datum +orafce_to_char_int4(PG_FUNCTION_ARGS) +{ + int32 arg0 = PG_GETARG_INT32(0); + StringInfo buf = makeStringInfo(); + + appendStringInfo(buf, "%d", arg0); + + PG_RETURN_TEXT_P(cstring_to_text(buf->data)); +} + +Datum +orafce_to_char_int8(PG_FUNCTION_ARGS) +{ + int64 arg0 = PG_GETARG_INT64(0); + StringInfo buf = makeStringInfo(); + + appendStringInfo(buf, INT64_FORMAT, arg0); + + PG_RETURN_TEXT_P(cstring_to_text(buf->data)); +} + +Datum +orafce_to_char_float4(PG_FUNCTION_ARGS) +{ + char *p; + char *result; + struct lconv *lconv = PGLC_localeconv(); + + result = DatumGetCString(DirectFunctionCall1(float4out, PG_GETARG_DATUM(0))); + + for (p = result; *p; p++) + if (*p == '.') + *p = lconv->decimal_point[0]; + + PG_RETURN_TEXT_P(cstring_to_text(result)); +} + +Datum +orafce_to_char_float8(PG_FUNCTION_ARGS) +{ + char *p; + char *result; + struct lconv *lconv = PGLC_localeconv(); + + result = DatumGetCString(DirectFunctionCall1(float8out, PG_GETARG_DATUM(0))); + + for (p = result; *p; p++) + if (*p == '.') + *p = lconv->decimal_point[0]; + + PG_RETURN_TEXT_P(cstring_to_text(result)); +} + +Datum +orafce_to_char_numeric(PG_FUNCTION_ARGS) +{ + Numeric arg0 = PG_GETARG_NUMERIC(0); + StringInfo buf = makeStringInfo(); + struct lconv *lconv = PGLC_localeconv(); + char *p; + char *decimal = NULL; + + appendStringInfoString(buf, DatumGetCString(DirectFunctionCall1(numeric_out, NumericGetDatum(arg0)))); + + for (p = buf->data; *p; p++) + if (*p == '.') + { + *p = lconv->decimal_point[0]; + decimal = p; /* save decimal point position for the next loop */ + } + + /* Simulate the default Oracle to_char template (TM9 - Text Minimum) + by removing unneeded digits after the decimal point; + if no digits are left, then remove the decimal point too + */ + for (p = buf->data + buf->len - 1; decimal && p >= decimal; p--) + { + if (*p == '0' || *p == lconv->decimal_point[0]) + *p = 0; + else + break; /* non-zero digit found, exit the loop */ + } + + PG_RETURN_TEXT_P(cstring_to_text(buf->data)); +} + +/******************************************************************** + * + * orafec_to_char_timestamp + * + * Syntax: + * + * text to_date(timestamp date_txt) + * + * Purpose: + * + * Returns date and time format w.r.t NLS_DATE_FORMAT GUC + * + *********************************************************************/ + +Datum +orafce_to_char_timestamp(PG_FUNCTION_ARGS) +{ + Timestamp ts = PG_GETARG_TIMESTAMP(0); + text *result = NULL; + + if(nls_date_format && strlen(nls_date_format) > 0) + { + /* it will return the DATE in nls_date_format*/ + result = DatumGetTextP(DirectFunctionCall2(timestamp_to_char, + TimestampGetDatum(ts), + CStringGetTextDatum(nls_date_format))); + } + else + { + result = cstring_to_text(DatumGetCString(DirectFunctionCall1(timestamp_out, + TimestampGetDatum(ts)))); + } + + PG_RETURN_TEXT_P(result); +} + +Datum +orafce_to_number(PG_FUNCTION_ARGS) +{ + text *arg0 = PG_GETARG_TEXT_PP(0); + char *buf; + struct lconv *lconv = PGLC_localeconv(); + Numeric res; + char *p; + + buf = text_to_cstring(arg0); + + for (p = buf; *p; p++) + if (*p == lconv->decimal_point[0] && lconv->decimal_point[0]) + *p = '.'; + else if (*p == lconv->thousands_sep[0] && lconv->thousands_sep[0]) + *p = ','; + + res = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(buf), 0, -1)); + + PG_RETURN_NUMERIC(res); +} + +/* 3 is enough, but it is defined as 4 in backend code. */ +#ifndef MAX_CONVERSION_GROWTH +#define MAX_CONVERSION_GROWTH 4 +#endif + +/* + * Convert a tilde (~) to ... + * 1: a full width tilde. (same as JA16EUCTILDE in oracle) + * 0: a full width overline. (same as JA16EUC in oracle) + * + * Note - there is a difference with Oracle - it returns \342\210\274 + * what is a tilde char. Orafce returns fullwidth tilde. If it is a + * problem, fix it for sef in code. + */ +#define JA_TO_FULL_WIDTH_TILDE 1 + +static const char * +TO_MULTI_BYTE_UTF8[95] = +{ + "\343\200\200", + "\357\274\201", + "\342\200\235", + "\357\274\203", + "\357\274\204", + "\357\274\205", + "\357\274\206", + "\342\200\231", + "\357\274\210", + "\357\274\211", + "\357\274\212", + "\357\274\213", + "\357\274\214", + "\357\274\215", + "\357\274\216", + "\357\274\217", + "\357\274\220", + "\357\274\221", + "\357\274\222", + "\357\274\223", + "\357\274\224", + "\357\274\225", + "\357\274\226", + "\357\274\227", + "\357\274\230", + "\357\274\231", + "\357\274\232", + "\357\274\233", + "\357\274\234", + "\357\274\235", + "\357\274\236", + "\357\274\237", + "\357\274\240", + "\357\274\241", + "\357\274\242", + "\357\274\243", + "\357\274\244", + "\357\274\245", + "\357\274\246", + "\357\274\247", + "\357\274\250", + "\357\274\251", + "\357\274\252", + "\357\274\253", + "\357\274\254", + "\357\274\255", + "\357\274\256", + "\357\274\257", + "\357\274\260", + "\357\274\261", + "\357\274\262", + "\357\274\263", + "\357\274\264", + "\357\274\265", + "\357\274\266", + "\357\274\267", + "\357\274\270", + "\357\274\271", + "\357\274\272", + "\357\274\273", + "\357\274\274", + "\357\274\275", + "\357\274\276", + "\357\274\277", + "\342\200\230", + "\357\275\201", + "\357\275\202", + "\357\275\203", + "\357\275\204", + "\357\275\205", + "\357\275\206", + "\357\275\207", + "\357\275\210", + "\357\275\211", + "\357\275\212", + "\357\275\213", + "\357\275\214", + "\357\275\215", + "\357\275\216", + "\357\275\217", + "\357\275\220", + "\357\275\221", + "\357\275\222", + "\357\275\223", + "\357\275\224", + "\357\275\225", + "\357\275\226", + "\357\275\227", + "\357\275\230", + "\357\275\231", + "\357\275\232", + "\357\275\233", + "\357\275\234", + "\357\275\235", +#if JA_TO_FULL_WIDTH_TILDE + "\357\275\236" +#else + "\357\277\243" +#endif +}; + +static const char * +TO_MULTI_BYTE_EUCJP[95] = +{ + "\241\241", + "\241\252", + "\241\311", + "\241\364", + "\241\360", + "\241\363", + "\241\365", + "\241\307", + "\241\312", + "\241\313", + "\241\366", + "\241\334", + "\241\244", + "\241\335", + "\241\245", + "\241\277", + "\243\260", + "\243\261", + "\243\262", + "\243\263", + "\243\264", + "\243\265", + "\243\266", + "\243\267", + "\243\270", + "\243\271", + "\241\247", + "\241\250", + "\241\343", + "\241\341", + "\241\344", + "\241\251", + "\241\367", + "\243\301", + "\243\302", + "\243\303", + "\243\304", + "\243\305", + "\243\306", + "\243\307", + "\243\310", + "\243\311", + "\243\312", + "\243\313", + "\243\314", + "\243\315", + "\243\316", + "\243\317", + "\243\320", + "\243\321", + "\243\322", + "\243\323", + "\243\324", + "\243\325", + "\243\326", + "\243\327", + "\243\330", + "\243\331", + "\243\332", + "\241\316", + "\241\357", + "\241\317", + "\241\260", + "\241\262", + "\241\306", /* Oracle returns different value \241\307 */ + "\243\341", + "\243\342", + "\243\343", + "\243\344", + "\243\345", + "\243\346", + "\243\347", + "\243\350", + "\243\351", + "\243\352", + "\243\353", + "\243\354", + "\243\355", + "\243\356", + "\243\357", + "\243\360", + "\243\361", + "\243\362", + "\243\363", + "\243\364", + "\243\365", + "\243\366", + "\243\367", + "\243\370", + "\243\371", + "\243\372", + "\241\320", + "\241\303", + "\241\321", +#if JA_TO_FULL_WIDTH_TILDE + "\241\301" +#else + "\241\261" +#endif +}; + +Datum +orafce_to_multi_byte(PG_FUNCTION_ARGS) +{ + text *src; + text *dst; + const char *s; + char *d; + int srclen; + +#if defined(_MSC_VER) && (defined(_M_X64) || defined(__amd64__)) + + __int64 dstlen; + +#else + + int dstlen; + + #endif + + int i; + const char **map; + + switch (GetDatabaseEncoding()) + { + case PG_UTF8: + map = TO_MULTI_BYTE_UTF8; + break; + case PG_EUC_JP: + case PG_EUC_JIS_2004: + map = TO_MULTI_BYTE_EUCJP; + break; + /* + * TODO: Add converter for encodings. + */ + default: /* no need to convert */ + PG_RETURN_DATUM(PG_GETARG_DATUM(0)); + } + + src = PG_GETARG_TEXT_PP(0); + s = VARDATA_ANY(src); + srclen = VARSIZE_ANY_EXHDR(src); + dst = (text *) palloc(VARHDRSZ + srclen * MAX_CONVERSION_GROWTH); + d = VARDATA(dst); + + for (i = 0; i < srclen; i++) + { + unsigned char u = (unsigned char) s[i]; + if (0x20 <= u && u <= 0x7e) + { + const char *m = map[u - 0x20]; + while (*m) + { + *d++ = *m++; + } + } + else + { + *d++ = s[i]; + } + } + + dstlen = d - VARDATA(dst); + SET_VARSIZE(dst, VARHDRSZ + dstlen); + + PG_RETURN_TEXT_P(dst); +} + +static int +getindex(const char **map, char *mbchar, int mblen) +{ + int i; + + for (i = 0; i < 95; i++) + { + if (!memcmp(map[i], mbchar, mblen)) + return i; + } + + return -1; +} + +Datum +orafce_to_single_byte(PG_FUNCTION_ARGS) +{ + text *src; + text *dst; + char *s; + char *d; + int srclen; + +#if defined(_MSC_VER) && (defined(_M_X64) || defined(__amd64__)) + + __int64 dstlen; + +#else + + int dstlen; + +#endif + + const char **map; + + switch (GetDatabaseEncoding()) + { + case PG_UTF8: + map = TO_MULTI_BYTE_UTF8; + break; + case PG_EUC_JP: + case PG_EUC_JIS_2004: + map = TO_MULTI_BYTE_EUCJP; + break; + /* + * TODO: Add converter for encodings. + */ + default: /* no need to convert */ + PG_RETURN_DATUM(PG_GETARG_DATUM(0)); + } + + src = PG_GETARG_TEXT_PP(0); + s = VARDATA_ANY(src); + srclen = VARSIZE_ANY_EXHDR(src); + + /* XXX - The output length should be <= input length */ + dst = (text *) palloc0(VARHDRSZ + srclen); + d = VARDATA(dst); + + while (*s && (s - VARDATA_ANY(src) < srclen)) + { + char *u = s; + int clen; + int mapindex; + + clen = pg_mblen(u); + s += clen; + + if (clen == 1) + *d++ = *u; + else if ((mapindex = getindex(map, u, clen)) >= 0) + { + const char m = 0x20 + mapindex; + *d++ = m; + } + else + { + memcpy(d, u, clen); + d += clen; + } + } + + dstlen = d - VARDATA(dst); + SET_VARSIZE(dst, VARHDRSZ + dstlen); + + PG_RETURN_TEXT_P(dst); +} + +/* convert hex digit (caller should have verified that) to value */ +static unsigned int +hexval(unsigned char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 0xA; + if (c >= 'A' && c <= 'F') + return c - 'A' + 0xA; + elog(ERROR, "invalid hexadecimal digit"); + return 0; /* not reached */ +} + + +/* + * First four chars should be hexnum digits + */ +static bool +isxdigit_four(const char *instr) +{ + return isxdigit((unsigned char) instr[0]) && + isxdigit((unsigned char) instr[1]) && + isxdigit((unsigned char) instr[2]) && + isxdigit((unsigned char) instr[3]); +} + +/* + * Translate string with hexadecimal digits to number + */ +static long int +hexval_four(const char *instr) +{ + return (hexval(instr[0]) << 12) + + (hexval(instr[1]) << 8) + + (hexval(instr[2]) << 4) + + hexval(instr[3]); +} + +#if PG_VERSION_NUM < 130000 + + +static bool +is_utf16_surrogate_first(pg_wchar c) +{ + return (c >= 0xD800 && c <= 0xDBFF); +} + +static bool +is_utf16_surrogate_second(pg_wchar c) +{ + return (c >= 0xDC00 && c <= 0xDFFF); +} + +static pg_wchar +surrogate_pair_to_codepoint(pg_wchar first, pg_wchar second) +{ + return ((first & 0x3FF) << 10) + 0x10000 + (second & 0x3FF); +} + +static inline bool +is_valid_unicode_codepoint(pg_wchar c) +{ + return (c > 0 && c <= 0x10FFFF); +} + +#define MAX_UNICODE_EQUIVALENT_STRING 16 + +/* + * Convert a single Unicode code point into a string in the server encoding. + * + * The code point given by "c" is converted and stored at *s, which must + * have at least MAX_UNICODE_EQUIVALENT_STRING+1 bytes available. + * The output will have a trailing '\0'. Throws error if the conversion + * cannot be performed. + * + * Note that this relies on having previously looked up any required + * conversion function. That's partly for speed but mostly because the parser + * may call this outside any transaction, or in an aborted transaction. + */ +static void +pg_unicode_to_server(pg_wchar c, unsigned char *s) +{ + unsigned char c_as_utf8[MAX_MULTIBYTE_CHAR_LEN + 1]; + int c_as_utf8_len; + int server_encoding; + + /* + * Complain if invalid Unicode code point. The choice of errcode here is + * debatable, but really our caller should have checked this anyway. + */ + if (!is_valid_unicode_codepoint(c)) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("invalid Unicode code point"))); + + /* Otherwise, if it's in ASCII range, conversion is trivial */ + if (c <= 0x7F) + { + s[0] = (unsigned char) c; + s[1] = '\0'; + return; + } + + /* If the server encoding is UTF-8, we just need to reformat the code */ + server_encoding = GetDatabaseEncoding(); + if (server_encoding == PG_UTF8) + { + unicode_to_utf8(c, s); + s[pg_utf_mblen(s)] = '\0'; + return; + } + + /* For all other cases, we must have a conversion function available */ + if (orafce_Utf8ToServerConvProc == NULL) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("conversion between %s and %s is not supported", + pg_enc2name_tbl[PG_UTF8].name, + GetDatabaseEncodingName()))); + + /* Construct UTF-8 source string */ + unicode_to_utf8(c, c_as_utf8); + c_as_utf8_len = pg_utf_mblen(c_as_utf8); + c_as_utf8[c_as_utf8_len] = '\0'; + + /* Convert, or throw error if we can't */ + FunctionCall5(orafce_Utf8ToServerConvProc, + Int32GetDatum(PG_UTF8), + Int32GetDatum(server_encoding), + CStringGetDatum(c_as_utf8), + CStringGetDatum(s), + Int32GetDatum(c_as_utf8_len)); +} + +static void +initializeUtf8ToServerConvProc(void) +{ + int current_server_encoding; + + orafce_Utf8ToServerConvProc = NULL; + + /* + * Also look up the UTF8-to-server conversion function if needed. Since + * the server encoding is fixed within any one backend process, we don't + * have to do this more than once. + */ + current_server_encoding = GetDatabaseEncoding(); + if (current_server_encoding != PG_UTF8 && + current_server_encoding != PG_SQL_ASCII) + { + Oid utf8_to_server_proc; + + utf8_to_server_proc = + FindDefaultConversionProc(PG_UTF8, + current_server_encoding); + /* If there's no such conversion, just leave the pointer as NULL */ + if (OidIsValid(utf8_to_server_proc)) + { + FmgrInfo *finfo; + + finfo = (FmgrInfo *) MemoryContextAlloc(TopMemoryContext, + sizeof(FmgrInfo)); + fmgr_info_cxt(utf8_to_server_proc, finfo, + TopMemoryContext); + /* Set Utf8ToServerConvProc only after data is fully valid */ + orafce_Utf8ToServerConvProc = finfo; + } + } +} + +#endif + +/* is Unicode code point acceptable? */ +static void +check_unicode_value(pg_wchar c) +{ + if (!is_valid_unicode_codepoint(c)) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("invalid Unicode escape value"))); +} + +/* + * Replaces unicode escape sequences by unicode chars + */ +Datum +orafce_unistr(PG_FUNCTION_ARGS) +{ + StringInfoData str; + text *input_text; + text *result; + pg_wchar pair_first = 0; + char cbuf[MAX_UNICODE_EQUIVALENT_STRING + 1]; + char *instr; + int len; + + /* when input string is NULL, then result is NULL too */ + if (PG_ARGISNULL(0)) + PG_RETURN_NULL(); + + input_text = PG_GETARG_TEXT_PP(0); + instr = VARDATA_ANY(input_text); + len = VARSIZE_ANY_EXHDR(input_text); + + initStringInfo(&str); + +#if PG_VERSION_NUM < 130000 + + initializeUtf8ToServerConvProc(); + +#endif + + while (len > 0) + { + if (instr[0] == '\\') + { + if (len >= 2 && + instr[1] == '\\') + { + if (pair_first) + goto invalid_pair; + appendStringInfoChar(&str, '\\'); + instr += 2; + len -= 2; + } + else if ((len >= 5 && isxdigit_four(&instr[1])) || + (len >= 6 && instr[1] == 'u' && isxdigit_four(&instr[2]))) + { + pg_wchar unicode; + int offset = instr[1] == 'u' ? 2 : 1; + + unicode = hexval_four(instr + offset); + + check_unicode_value(unicode); + + if (pair_first) + { + if (is_utf16_surrogate_second(unicode)) + { + unicode = surrogate_pair_to_codepoint(pair_first, unicode); + pair_first = 0; + } + else + goto invalid_pair; + } + else if (is_utf16_surrogate_second(unicode)) + goto invalid_pair; + + if (is_utf16_surrogate_first(unicode)) + pair_first = unicode; + else + { + pg_unicode_to_server(unicode, (unsigned char *) cbuf); + appendStringInfoString(&str, cbuf); + } + + instr += 4 + offset; + len -= 4 + offset; + } + else if (len >= 8 && + instr[1] == '+' && + isxdigit_four(&instr[2]) && + isxdigit((unsigned char) instr[6]) && + isxdigit((unsigned char) instr[7])) + { + pg_wchar unicode; + + unicode = (hexval_four(&instr[2]) << 8) + + (hexval(instr[6]) << 4) + + hexval(instr[7]); + + check_unicode_value(unicode); + + if (pair_first) + { + if (is_utf16_surrogate_second(unicode)) + { + unicode = surrogate_pair_to_codepoint(pair_first, unicode); + pair_first = 0; + } + else + goto invalid_pair; + } + else if (is_utf16_surrogate_second(unicode)) + goto invalid_pair; + + if (is_utf16_surrogate_first(unicode)) + pair_first = unicode; + else + { + pg_unicode_to_server(unicode, (unsigned char *) cbuf); + appendStringInfoString(&str, cbuf); + } + + instr += 8; + len -= 8; + } + else if (len >= 10 && + instr[1] == 'U' && + isxdigit_four(&instr[2]) && + isxdigit_four(&instr[6])) + { + pg_wchar unicode; + + unicode = (hexval_four(&instr[2]) << 16) + hexval_four(&instr[6]); + + check_unicode_value(unicode); + + if (pair_first) + { + if (is_utf16_surrogate_second(unicode)) + { + unicode = surrogate_pair_to_codepoint(pair_first, unicode); + pair_first = 0; + } + else + goto invalid_pair; + } + else if (is_utf16_surrogate_second(unicode)) + goto invalid_pair; + + if (is_utf16_surrogate_first(unicode)) + pair_first = unicode; + else + { + pg_unicode_to_server(unicode, (unsigned char *) cbuf); + appendStringInfoString(&str, cbuf); + } + + instr += 10; + len -= 10; + } + else + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("invalid Unicode escape"), + errhint("Unicode escapes must be \\XXXX, \\+XXXXXX, \\uXXXX or \\UXXXXXXXX."))); + } + else + { + if (pair_first) + goto invalid_pair; + + appendStringInfoChar(&str, *instr++); + len--; + } + } + + /* unfinished surrogate pair? */ + if (pair_first) + goto invalid_pair; + + result = cstring_to_text_with_len(str.data, str.len); + pfree(str.data); + + PG_RETURN_TEXT_P(result); + +invalid_pair: + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("invalid Unicode surrogate pair"))); +} diff --git a/src/postgres/third-party-extensions/orafce/datefce.c b/src/postgres/third-party-extensions/orafce/datefce.c new file mode 100644 index 000000000000..6ec781a5a128 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/datefce.c @@ -0,0 +1,1060 @@ +#include "postgres.h" +#include "access/xact.h" +#include "commands/variable.h" +#include "mb/pg_wchar.h" +#include "utils/date.h" +#include "utils/builtins.h" +#include "utils/numeric.h" +#include "utils/formatting.h" +#include +#include "orafce.h" +#include "builtins.h" + +#define ENABLE_INTERNATIONALIZED_WEEKDAY + +#ifdef ENABLE_INTERNATIONALIZED_WEEKDAY + +typedef struct WeekDays +{ + int encoding; + const char *names[7]; +} WeekDays; + +/* + * { encoding, { "sun", "mon", "tue", "wed", "thu", "fri", "sat" } }, + */ +static const WeekDays WEEKDAYS[] = +{ + /* Japanese, UTF8 */ + { PG_UTF8, { "\346\227\245", "\346\234\210", "\347\201\253", "\346\260\264", "\346\234\250", "\351\207\221", "\345\234\237" } }, + /* Japanese, EUC_JP */ + { PG_EUC_JP, { "\306\374", "\267\356", "\262\320", "\277\345", "\314\332", "\266\342", "\305\332" } }, + /* Japanese, EUC_JIS_2004 (same as EUC_JP) */ + { PG_EUC_JIS_2004, { "\306\374", "\267\356", "\262\320", "\277\345", "\314\332", "\266\342", "\305\332" } }, +}; + +static const WeekDays *mru_weekdays = NULL; + +static int +weekday_search(const WeekDays *weekdays, const char *str, size_t len) +{ + int i; + + for (i = 0; i < 7; i++) + { + size_t n = strlen(weekdays->names[i]); + if (n > len) + continue; /* too short */ + if (pg_strncasecmp(weekdays->names[i], str, n) == 0) + return i; + } + return -1; /* not found */ +} + +#endif /* ENABLE_INTERNATIONALIZED_WEEKDAY */ + +#define CASE_fmt_YYYY case 0: case 1: case 2: case 3: case 4: case 5: case 6: +#define CASE_fmt_IYYY case 7: case 8: case 9: case 10: +#define CASE_fmt_Q case 11: +#define CASE_fmt_WW case 12: +#define CASE_fmt_IW case 13: +#define CASE_fmt_W case 14: +#define CASE_fmt_DAY case 15: case 16: case 17: +#define CASE_fmt_MON case 18: case 19: case 20: case 21: +#define CASE_fmt_CC case 22: case 23: +#define CASE_fmt_DDD case 24: case 25: case 26: +#define CASE_fmt_HH case 27: case 28: case 29: +#define CASE_fmt_MI case 30: + +STRING_PTR_FIELD_TYPE date_fmt[] = +{ + "Y", "Yy", "Yyy", "Yyyy", "Year", "Syyyy", "syear", + "I", "Iy", "Iyy", "Iyyy", + "Q", "Ww", "Iw", "W", + "Day", "Dy", "D", + "Month", "Mon", "Mm", "Rm", + "Cc", "Scc", + "Ddd", "Dd", "J", + "Hh", "Hh12", "Hh24", + "Mi", + NULL +}; + +STRING_PTR_FIELD_TYPE ora_days[] = {"Sunday", "Monday", "Tuesday", "Wednesday", +"Thursday", "Friday", "Saturday", NULL}; + +#define CHECK_SEQ_SEARCH(_l, _s) \ +do { \ + if ((_l) < 0) { \ + ereport(ERROR, \ + (errcode(ERRCODE_INVALID_DATETIME_FORMAT), \ + errmsg("invalid value for %s", (_s)))); \ + } \ +} while (0) + +PG_FUNCTION_INFO_V1(next_day); +PG_FUNCTION_INFO_V1(next_day_by_index); +PG_FUNCTION_INFO_V1(last_day); +PG_FUNCTION_INFO_V1(months_between); +PG_FUNCTION_INFO_V1(add_months); +PG_FUNCTION_INFO_V1(ora_to_date); +PG_FUNCTION_INFO_V1(ora_date_trunc); +PG_FUNCTION_INFO_V1(ora_date_round); +PG_FUNCTION_INFO_V1(ora_timestamptz_trunc); +PG_FUNCTION_INFO_V1(ora_timestamptz_round); +PG_FUNCTION_INFO_V1(ora_timestamp_trunc); +PG_FUNCTION_INFO_V1(ora_timestamp_round); +PG_FUNCTION_INFO_V1(orafce_sysdate); +PG_FUNCTION_INFO_V1(orafce_sessiontimezone); +PG_FUNCTION_INFO_V1(orafce_dbtimezone); + +/* + * Search const value in char array + * + */ + +int +ora_seq_search(const char *name, STRING_PTR_FIELD_TYPE array[], size_t max) +{ + int i; + + if (!*name) + return -1; + + for (i = 0; array[i]; i++) + { + if (strlen(array[i]) == max && + pg_strncasecmp(name, array[i], max) == 0) + return i; + } + return -1; /* not found */ +} + +static int +ora_seq_prefix_search(const char *name, STRING_PTR_FIELD_TYPE array[], int max) +{ + int i; + + if (!*name) + return -1; + + for (i = 0; array[i]; i++) + { + if (pg_strncasecmp(name, array[i], max) == 0) + return i; + } + return -1; /* not found */ +} + +/******************************************************************** + * + * next_day + * + * Syntax: + * + * date next_day(date value, text weekday) + * + * Purpose: + * + * Returns the first weekday that is greater than a date value. + * + ********************************************************************/ + + +Datum +next_day(PG_FUNCTION_ARGS) +{ + DateADT day = PG_GETARG_DATEADT(0); + text *day_txt = PG_GETARG_TEXT_PP(1); + const char *str = VARDATA_ANY(day_txt); + int len = VARSIZE_ANY_EXHDR(day_txt); + int off; + int d = -1; + +#ifdef ENABLE_INTERNATIONALIZED_WEEKDAY + /* Check mru_weekdays first for performance. */ + if (mru_weekdays) + { + if ((d = weekday_search(mru_weekdays, str, len)) >= 0) + goto found; + else + mru_weekdays = NULL; + } +#endif + + /* + * Oracle uses only 3 heading characters of the input. + * Ignore all trailing characters. + */ + if (len >= 3 && (d = ora_seq_prefix_search(str, ora_days, 3)) >= 0) + goto found; + +#ifdef ENABLE_INTERNATIONALIZED_WEEKDAY + do + { + int i; + int encoding = GetDatabaseEncoding(); + + for (i = 0; i < (int) lengthof(WEEKDAYS); i++) + { + if (encoding == WEEKDAYS[i].encoding) + { + if ((d = weekday_search(&WEEKDAYS[i], str, len)) >= 0) + { + mru_weekdays = &WEEKDAYS[i]; + goto found; + } + } + } + } while(0); +#endif + + CHECK_SEQ_SEARCH(-1, "DAY/Day/day"); + +found: + off = d - j2day(day+POSTGRES_EPOCH_JDATE); + + PG_RETURN_DATEADT((off <= 0) ? day+off+7 : day + off); +} + +/* next_day(date, integer) is not documented in Oracle manual, but ... */ +Datum +next_day_by_index(PG_FUNCTION_ARGS) +{ + DateADT day = PG_GETARG_DATEADT(0); + int idx = PG_GETARG_INT32(1); + int off; + + /* + * off is 1..7 (Sun..Sat). + * + * TODO: It should be affected by NLS_TERRITORY. For example, + * 1..7 should be interpreted as Mon..Sun in GERMAN. + */ + CHECK_SEQ_SEARCH((idx < 1 || 7 < idx) ? -1 : 0, "DAY/Day/day"); + + /* j2day returns 0..6 as Sun..Sat */ + off = (idx - 1) - j2day(day+POSTGRES_EPOCH_JDATE); + + PG_RETURN_DATEADT((off <= 0) ? day+off+7 : day + off); +} + +/******************************************************************** + * + * last_day + * + * Syntax: + * + * date last_day(date value) + * + * Purpose: + * + * Returns last day of the month based on a date value + * + ********************************************************************/ + + +Datum +last_day(PG_FUNCTION_ARGS) +{ + DateADT day = PG_GETARG_DATEADT(0); + DateADT result; + int y, m, d; + j2date(day + POSTGRES_EPOCH_JDATE, &y, &m, &d); + result = date2j(y, m+1, 1) - POSTGRES_EPOCH_JDATE; + + PG_RETURN_DATEADT(result - 1); +} + +static const int month_days[] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +static int +days_of_month(int y, int m) +{ + int days; + + if (m < 0 || 12 < m) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("date out of range"))); + + days = month_days[m - 1]; + if (m == 2 && (y % 400 == 0 || (y % 4 == 0 && y % 100 != 0))) + days += 1; /* February 29 in leap year */ + return days; +} + +/******************************************************************** + * + * months_between + * + * Syntax: + * + * numeric months_between(date date1, date date2) + * + * Purpose: + * + * Returns the number of months between date1 and date2. If + * a fractional month is calculated, the months_between function + * calculates the fraction based on a 31-day month. + * + ********************************************************************/ + +Datum +months_between(PG_FUNCTION_ARGS) +{ + DateADT date1 = PG_GETARG_DATEADT(0); + DateADT date2 = PG_GETARG_DATEADT(1); + + int y1, m1, d1; + int y2, m2, d2; + + float8 result; + + j2date(date1 + POSTGRES_EPOCH_JDATE, &y1, &m1, &d1); + j2date(date2 + POSTGRES_EPOCH_JDATE, &y2, &m2, &d2); + + /* Ignore day components for last days, or based on a 31-day month. */ + if (d1 == days_of_month(y1, m1) && d2 == days_of_month(y2, m2)) + result = (y1 - y2) * 12 + (m1 - m2); + else + result = (y1 - y2) * 12 + (m1 - m2) + (d1 - d2) / 31.0; + + PG_RETURN_NUMERIC( + DirectFunctionCall1(float8_numeric, Float8GetDatumFast(result))); +} + +/******************************************************************** + * + * add_months + * + * Syntax: + * + * date add_months(date day, int val) + * + * Purpose: + * + * Returns a date plus n months. + * + ********************************************************************/ + + +Datum +add_months(PG_FUNCTION_ARGS) +{ + DateADT day = PG_GETARG_DATEADT(0); + int n = PG_GETARG_INT32(1); + int y, m, d; + int days; + DateADT result; + div_t v; + bool last_day; + + j2date(day + POSTGRES_EPOCH_JDATE, &y, &m, &d); + last_day = (d == days_of_month(y, m)); + + v = div(y * 12 + m - 1 + n, 12); + y = v.quot; + if (y < 0) + y += 1; /* offset because of year 0 */ + m = v.rem + 1; + + days = days_of_month(y, m); + if (last_day || d > days) + d = days; + + result = date2j(y, m, d) - POSTGRES_EPOCH_JDATE; + + PG_RETURN_DATEADT (result); +} + +/* + * ISO year + * + */ + +#define DATE2J(y,m,d) (date2j((y),(m),(d)) - POSTGRES_EPOCH_JDATE) +#define J2DAY(date) (j2day(date + POSTGRES_EPOCH_JDATE)) + + +static DateADT +iso_year (int y, int m, int d) +{ + DateADT result, result2, day; + int off; + + result = DATE2J(y,1,1); + day = DATE2J(y,m,d); + off = 4 - J2DAY(result); + result += off + ((off >= 0) ? - 3: + 4); /* to monday */ + + if (result > day) + { + result = DATE2J(y-1,1,1); + off = 4 - J2DAY(result); + result += off + ((off >= 0) ? - 3: + 4); /* to monday */ + } + + if (((day - result) / 7 + 1) > 52) + { + result2 = DATE2J(y+1,1,1); + off = 4 - J2DAY(result2); + result2 += off + ((off >= 0) ? - 3: + 4); /* to monday */ + + if (day >= result2) + return result2; + } + + return result; +} + +static DateADT +_ora_date_trunc(DateADT day, int f) +{ + int y, m, d; + DateADT result; + + j2date(day + POSTGRES_EPOCH_JDATE, &y, &m, &d); + + switch (f) + { + CASE_fmt_CC + if (y > 0) + result = DATE2J((y/100)*100+1,1,1); + else + result = DATE2J(-((99 - (y - 1)) / 100) * 100 + 1,1,1); + break; + CASE_fmt_YYYY + result = DATE2J(y,1,1); + break; + CASE_fmt_IYYY + result = iso_year(y,m,d); + break; + CASE_fmt_MON + result = DATE2J(y,m,1); + break; + CASE_fmt_WW + result = day - (day - DATE2J(y,1,1)) % 7; + break; + CASE_fmt_IW + result = day - (day - iso_year(y,m,d)) % 7; + break; + CASE_fmt_W + result = day - (day - DATE2J(y,m,1)) % 7; + break; + CASE_fmt_DAY + result = day - J2DAY(day); + break; + CASE_fmt_Q + result = DATE2J(y,((m-1)/3)*3+1,1); + break; + default: + result = day; + } + + return result; +} + +static DateADT +_ora_date_round(DateADT day, int f) +{ + int y, m, d, z; + DateADT result; + + j2date(day + POSTGRES_EPOCH_JDATE, &y, &m, &d); + + switch (f) + { + CASE_fmt_CC + if (y > 0) + result = DATE2J((y/100)*100+(day < DATE2J((y/100)*100+50,1,1) ?1:101),1,1); + else + result = DATE2J((y/100)*100+(day < DATE2J((y/100)*100-50+1,1,1) ?-99:1),1,1); + break; + CASE_fmt_YYYY + result = DATE2J(y+(day= 52) + { + bool overl = ((date2j(y+2,1,1)-date2j(y+1,1,1)) == 366); + bool isSaturday = (J2DAY(day) == 6); + + DateADT iy2 = iso_year(y+2, 1, 8); + DateADT day1 = DATE2J(y+1,1,1); + /* exception saturdays */ + if (iy1 >= (day1) && day >= day1 - 2 && isSaturday) + { + result = overl?iy2:iy1; + } + /* iso year stars in last year and day >= iso year */ + else if (iy1 <= (day1) && day >= iy1 - 3) + { + DateADT cmp = iy1 - (iy1 < day1?0:1); + int d = J2DAY(day1); + /* some exceptions */ + if ((day >= cmp - 2) && (!(d == 3 && overl))) + { + /* if year don't starts in thursday */ + if ((d < 4 && J2DAY(day) != 5 && !isSaturday) + ||(d == 2 && isSaturday && overl)) + { + result = iy2; + } + } + } + } + } + break; + } + CASE_fmt_MON + result = DATE2J(y,m+(day= 52) + { + /* only for last iso week */ + DateADT isoyear = iso_year(y+1, 1, 8); + if (isoyear > (DATE2J(y+1,1,1)-1)) + if (day > isoyear - 7) + { + int d = J2DAY(day); + result -= (d == 0 || d > 4?7:0); + } + } + break; + } + CASE_fmt_W + z = (day - DATE2J(y,m,1)) % 7; + result = day - z + (z < 4?0:7); + break; + CASE_fmt_DAY + z = J2DAY(day); + if (y > 0) + result = day - z + (z < 4?0:7); + else + result = day + (5 - (z>0?(z>1?z:z+7):7)); + break; + CASE_fmt_Q + result = DATE2J(y,((m-1)/3)*3+(day<(DATE2J(y,((m-1)/3)*3+2,16))?1:4),1); + break; + default: + result = day; + } + return result; +} + +/******************************************************************** + * + * ora_to_date + * + * Syntax: + * + * timestamp to_date(text date_txt) + * + * Purpose: + * + * Returns date and time format w.r.t NLS_DATE_FORMAT GUC + * + ********************************************************************/ + +Datum +ora_to_date(PG_FUNCTION_ARGS) +{ + text *date_txt = PG_GETARG_TEXT_PP(0); + Timestamp result; + + if(nls_date_format && strlen(nls_date_format)) + { + Datum newDate; + + /* it will return timestamp at GMT */ + newDate = DirectFunctionCall2(to_timestamp, + PointerGetDatum(date_txt), + CStringGetTextDatum(nls_date_format)); + + /* convert to local timestamp */ + result = DatumGetTimestamp(DirectFunctionCall1(timestamptz_timestamp, newDate)); + } + else + result = DatumGetTimestamp(DirectFunctionCall3(timestamp_in, + CStringGetDatum(text_to_cstring(date_txt)), + ObjectIdGetDatum(InvalidOid), + Int32GetDatum(-1))); + + PG_RETURN_TIMESTAMP(result); +} + +/******************************************************************** + * + * ora_date_trunc|ora_timestamptz_trunc .. trunc + * + * Syntax: + * + * date trunc(date date1, text format) + * + * Purpose: + * + * Returns d with the time portion of the day truncated to the unit + * specified by the format fmt. + * + ********************************************************************/ + +Datum +ora_date_trunc(PG_FUNCTION_ARGS) +{ + DateADT day = PG_GETARG_DATEADT(0); + text *fmt = PG_GETARG_TEXT_PP(1); + + DateADT result; + + int f = ora_seq_search(VARDATA_ANY(fmt), date_fmt, VARSIZE_ANY_EXHDR(fmt)); + CHECK_SEQ_SEARCH(f, "round/trunc format string"); + + result = _ora_date_trunc(day, f); + PG_RETURN_DATEADT(result); +} + +/* + * Workaround for access to session_timezone on WIN32, + * + * session timezone isn't accessed directly, but taken by show_timezone, + * and reparsed. For better performance, the result is cached in fn_extra. + * + */ +static pg_tz * +get_session_timezone(FunctionCallInfo fcinfo) +{ +#if defined(WIN32) + + pg_tz *result = (pg_tz *) fcinfo->flinfo->fn_extra; + + if (result == NULL) + { + const char *tzn = show_timezone(); + void *extra; + + if (!check_timezone(((char **) &tzn), &extra, PGC_S_CLIENT)) + elog(ERROR, "cannot to parse timezone \"%s\"", tzn); + + result = *((pg_tz **) extra); + fcinfo->flinfo->fn_extra = result; + + /* + * check_timezone allocates small block of pg_tz * size. This block + * should be released by free(extra), but I cannot release memory + * allocated by application in library on MS platform. So I have to + * accept small memory leak - elsewhere exception - broken heap :( + * + * + * cannot be called + free( extra ); + */ + } + + return result; + +#else + + return session_timezone; + +#endif +} + +#define TRUNC_DAY tm->tm_hour = 0; tm->tm_min = 0; *redotz = true; + +/* + * redotz is used only for timestamp with time zone + */ +static void +tm_trunc(struct pg_tm *tm, text *fmt, bool *redotz) +{ + int f; + + f = ora_seq_search(VARDATA_ANY(fmt), date_fmt, VARSIZE_ANY_EXHDR(fmt)); + CHECK_SEQ_SEARCH(f, "round/trunc format string"); + + tm->tm_sec = 0; + + switch (f) + { + CASE_fmt_IYYY + CASE_fmt_WW + CASE_fmt_W + CASE_fmt_IW + CASE_fmt_DAY + CASE_fmt_CC + j2date(_ora_date_trunc(DATE2J(tm->tm_year, tm->tm_mon, tm->tm_mday), f) + + POSTGRES_EPOCH_JDATE, + &tm->tm_year, &tm->tm_mon, &tm->tm_mday); + TRUNC_DAY; + break; + + CASE_fmt_YYYY + tm->tm_mon = 1; + tm->tm_mday = 1; + TRUNC_DAY; + break; + + CASE_fmt_Q + tm->tm_mon = (3*((tm->tm_mon - 1)/3)) + 1; + tm->tm_mday = 1; + TRUNC_DAY; + break; + + CASE_fmt_MON + tm->tm_mday = 1; + TRUNC_DAY; + break; + + CASE_fmt_DDD + TRUNC_DAY; + break; + + CASE_fmt_HH + tm->tm_min = 0; + break; + } +} + +Datum +ora_timestamptz_trunc(PG_FUNCTION_ARGS) +{ + TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0); + TimestampTz result; + text *fmt = PG_GETARG_TEXT_PP(1); + int tz; + fsec_t fsec; + struct pg_tm tt, *tm = &tt; + const char *tzn; + bool redotz = false; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + PG_RETURN_TIMESTAMPTZ(timestamp); + + if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range"))); + + tm_trunc(tm, fmt, &redotz); + fsec = 0; + + if (redotz) + tz = DetermineTimeZoneOffset(tm, get_session_timezone(fcinfo)); + + if (tm2timestamp(tm, fsec , &tz, &result) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range"))); + + PG_RETURN_TIMESTAMPTZ(result); +} + +/******************************************************************** + * + * ora_date_round|ora_timestamptz_round .. round + * + * Syntax: + * + * date round(date date1, text format) + * + * Purpose: + * + * Returns d with the time portion of the day roundeded to the unit + * specified by the format fmt. + * + ********************************************************************/ + + +Datum +ora_date_round(PG_FUNCTION_ARGS) +{ + DateADT day = PG_GETARG_DATEADT(0); + text *fmt = PG_GETARG_TEXT_PP(1); + + DateADT result; + + int f = ora_seq_search(VARDATA_ANY(fmt), date_fmt, VARSIZE_ANY_EXHDR(fmt)); + CHECK_SEQ_SEARCH(f, "round/trunc format string"); + + result = _ora_date_round(day, f); + PG_RETURN_DATEADT(result); +} + +#define NOT_ROUND_MDAY(_p_) \ + do { if (_p_) rounded = false; } while(0) +#define ROUND_MDAY(_tm_) \ + do { if (rounded) _tm_->tm_mday += _tm_->tm_hour >= 12?1:0; } while(0) + +static void +tm_round(struct pg_tm *tm, text *fmt, bool *redotz) +{ + int f; + bool rounded = true; + + f = ora_seq_search(VARDATA_ANY(fmt), date_fmt, VARSIZE_ANY_EXHDR(fmt)); + CHECK_SEQ_SEARCH(f, "round/trunc format string"); + + /* set rounding rule */ + switch (f) + { + CASE_fmt_IYYY + NOT_ROUND_MDAY(tm->tm_mday < 8 && tm->tm_mon == 1); + NOT_ROUND_MDAY(tm->tm_mday == 30 && tm->tm_mon == 6); + if (tm->tm_mday >= 28 && tm->tm_mon == 12 && tm->tm_hour >= 12) + { + DateADT isoyear = iso_year(tm->tm_year+1, 1, 8); + DateADT day0 = DATE2J(tm->tm_year+1,1,1); + DateADT dayc = DATE2J(tm->tm_year, tm->tm_mon, tm->tm_mday); + + if ((isoyear <= day0) || (day0 <= dayc + 2)) + { + rounded = false; + } + } + break; + CASE_fmt_YYYY + NOT_ROUND_MDAY(tm->tm_mday == 30 && tm->tm_mon == 6); + break; + CASE_fmt_MON + NOT_ROUND_MDAY(tm->tm_mday == 15); + break; + CASE_fmt_Q + NOT_ROUND_MDAY(tm->tm_mday == 15 && tm->tm_mon == ((tm->tm_mon-1)/3)*3+2); + break; + CASE_fmt_WW + CASE_fmt_IW + /* last day in year */ + NOT_ROUND_MDAY(DATE2J(tm->tm_year, tm->tm_mon, tm->tm_mday) == + (DATE2J(tm->tm_year+1, 1,1) - 1)); + break; + CASE_fmt_W + /* last day in month */ + NOT_ROUND_MDAY(DATE2J(tm->tm_year, tm->tm_mon, tm->tm_mday) == + (DATE2J(tm->tm_year, tm->tm_mon+1,1) - 1)); + break; + } + + switch (f) + { + /* easier convert to date */ + CASE_fmt_IW + CASE_fmt_DAY + CASE_fmt_IYYY + CASE_fmt_WW + CASE_fmt_W + CASE_fmt_CC + CASE_fmt_MON + CASE_fmt_YYYY + CASE_fmt_Q + ROUND_MDAY(tm); + j2date(_ora_date_round(DATE2J(tm->tm_year, tm->tm_mon, tm->tm_mday), f) + + POSTGRES_EPOCH_JDATE, + &tm->tm_year, &tm->tm_mon, &tm->tm_mday); + tm->tm_hour = 0; + tm->tm_min = 0; + *redotz = true; + break; + CASE_fmt_DDD + tm->tm_mday += (tm->tm_hour >= 12)?1:0; + tm->tm_hour = 0; + tm->tm_min = 0; + *redotz = true; + break; + CASE_fmt_MI + tm->tm_min += (tm->tm_sec >= 30)?1:0; + break; + CASE_fmt_HH + tm->tm_hour += (tm->tm_min >= 30)?1:0; + tm->tm_min = 0; + break; + } + + tm->tm_sec = 0; +} + +Datum +ora_timestamptz_round(PG_FUNCTION_ARGS) +{ + TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0); + TimestampTz result; + text *fmt = PG_GETARG_TEXT_PP(1); + int tz; + fsec_t fsec; + struct pg_tm tt, *tm = &tt; + const char *tzn; + bool redotz = false; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + PG_RETURN_TIMESTAMPTZ(timestamp); + + if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range"))); + + tm_round(tm, fmt, &redotz); + fsec = 0; + + if (redotz) + tz = DetermineTimeZoneOffset(tm, get_session_timezone(fcinfo)); + + if (tm2timestamp(tm, fsec, &tz, &result) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range"))); + + PG_RETURN_TIMESTAMPTZ(result); +} + +Datum +ora_timestamp_trunc(PG_FUNCTION_ARGS) +{ + Timestamp timestamp = PG_GETARG_TIMESTAMP(0); + Timestamp result; + text *fmt = PG_GETARG_TEXT_PP(1); + fsec_t fsec; + struct pg_tm tt, *tm = &tt; + bool redotz = false; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + PG_RETURN_TIMESTAMP(timestamp); + + if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range"))); + + tm_trunc(tm, fmt, &redotz); + fsec = 0; + + if (tm2timestamp(tm, fsec, NULL, &result) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range"))); + + PG_RETURN_TIMESTAMP(result); +} + +Datum +ora_timestamp_round(PG_FUNCTION_ARGS) +{ + Timestamp timestamp = PG_GETARG_TIMESTAMP(0); + Timestamp result; + text *fmt = PG_GETARG_TEXT_PP(1); + fsec_t fsec; + struct pg_tm tt, *tm = &tt; + bool redotz = false; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + PG_RETURN_TIMESTAMPTZ(timestamp); + + if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range"))); + + tm_round(tm, fmt, &redotz); + + if (tm2timestamp(tm, fsec, NULL, &result) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range"))); + + PG_RETURN_TIMESTAMP(result); +} + +/******************************************************************** + * + * ora_sysdate - sysdate + * + * Syntax: + * + * timestamp sysdate() + * + * Purpose: + * + * Returns statement_timestamp in server time zone + * Note - server time zone doesn't exists on PostgreSQL - emulated + * by orafce_timezone + * + ********************************************************************/ + +Datum +orafce_sysdate(PG_FUNCTION_ARGS) +{ + Datum sysdate; + Datum sysdate_scaled; + + + sysdate = DirectFunctionCall2(timestamptz_zone, + CStringGetTextDatum(orafce_timezone), + TimestampTzGetDatum(GetCurrentStatementStartTimestamp())); + + /* necessary to cast to timestamp(0) to emulate Oracle's date */ + sysdate_scaled = DirectFunctionCall2(timestamp_scale, + sysdate, + Int32GetDatum(0)); + + PG_RETURN_DATUM(sysdate_scaled); +} + +/******************************************************************** + * + * ora_systemtimezone + * + * Syntax: + * + * text sessiontimezone() + * + * Purpose: + * + * Returns session time zone + * + ********************************************************************/ + +Datum +orafce_sessiontimezone(PG_FUNCTION_ARGS) +{ + PG_RETURN_TEXT_P(cstring_to_text(show_timezone())); +} + +/******************************************************************** + * + * ora_dbtimezone + * + * Syntax: + * + * text dbtimezone() + * + * Purpose: + * + * Returns server time zone - emulated by orafce_timezone + * + ********************************************************************/ + +Datum +orafce_dbtimezone(PG_FUNCTION_ARGS) +{ + PG_RETURN_TEXT_P(cstring_to_text(orafce_timezone)); +} diff --git a/src/postgres/third-party-extensions/orafce/expected/dbms_alert_session_A.out b/src/postgres/third-party-extensions/orafce/expected/dbms_alert_session_A.out new file mode 100644 index 000000000000..534d5e3042f0 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/expected/dbms_alert_session_A.out @@ -0,0 +1,165 @@ +\set ECHO all +SELECT pg_sleep(3); + pg_sleep +---------- + +(1 row) + +/* + * DBMS_ALERT is used for one-way communication of one session to other. + * + * This session mainly sends signals for testing the alert functionality in + * session B and C. + * + * The following alerts are used to ensure that signals are sent at correct + * times to session B for testing. These signals are sent from session B + * indicating completion of an event. + * After the signal is received, the next required signal for testing is sent + * from this session. + */ +SELECT dbms_alert.register('b1'); + register +---------- + +(1 row) + +SELECT dbms_alert.register('b2'); + register +---------- + +(1 row) + +SELECT dbms_alert.register('b3'); + register +---------- + +(1 row) + +SELECT dbms_alert.register('b4'); + register +---------- + +(1 row) + +SELECT dbms_alert.register('b5'); + register +---------- + +(1 row) + +SELECT dbms_alert.signal('a1','Msg1 for a1'); + signal +-------- + +(1 row) + +SELECT dbms_alert.signal('a2','Msg1 for a2'); + signal +-------- + +(1 row) + +/* + * Test: defered_signal + * The signal is received only when the signalling transaction commits. + * To test this, an explict BEGIN-COMMIT block is used. + */ +SELECT dbms_alert.signal('tds','Begin defered_signal test'); + signal +-------- + +(1 row) + +BEGIN; +SELECT dbms_alert.signal('tds','Testing defered_signal'); + signal +-------- + +(1 row) + +/* The signal is received while transaction is running */ +SELECT dbms_alert.waitone('b1',20); + waitone +--------------------------------- + ("Transaction still running",0) +(1 row) + +COMMIT; +/* The signal is received after transaction completed. + * After this the tds signal is received in session B indicating that the + * signal is received only after commit. + */ +SELECT dbms_alert.waitone('b1',20); + waitone +----------------------------- + ("Transaction committed",0) +(1 row) + +SELECT dbms_alert.waitone('b2',20); + waitone +---------------------------------------- + ("to check unregistered alert wait",0) +(1 row) + +/* This signals a3 which is not registered in Session B */ +SELECT dbms_alert.signal('a3','Msg1 for a3'); + signal +-------- + +(1 row) + +/* alert a4 is signalled soon after a3 */ +SELECT dbms_alert.signal('a4','Test- Register after signal'); + signal +-------- + +(1 row) + +/* This signal indicates at remove() is called */ +SELECT dbms_alert.waitone('b3',20); + waitone +------------------------- + ("remove(a1) called",0) +(1 row) + +/* Send signal which is removed in session B */ +SELECT dbms_alert.signal('a1','Msg2 for a1'); + signal +-------- + +(1 row) + +SELECT dbms_alert.waitone('b4',20); + waitone +-------------------------------- + ("to check unremoved alert",0) +(1 row) + +/* Send signal which is registered in B and not removed */ +SELECT dbms_alert.signal('a4','Msg1 for a4'); + signal +-------- + +(1 row) + +/* This signal inidcates that removeall() is called */ +SELECT dbms_alert.waitone('b5',20); + waitone +------------------------ + ("removeall called",0) +(1 row) + +/* Send a signal to test if session B receives it after removeall() */ +SELECT dbms_alert.signal('a2','Msg2 for a2'); + signal +-------- + +(1 row) + +/* cleanup */ +SELECT dbms_alert.removeall(); + removeall +----------- + +(1 row) + diff --git a/src/postgres/third-party-extensions/orafce/expected/dbms_alert_session_B.out b/src/postgres/third-party-extensions/orafce/expected/dbms_alert_session_B.out new file mode 100644 index 000000000000..bc170ea271d6 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/expected/dbms_alert_session_B.out @@ -0,0 +1,151 @@ +\set ECHO all +/* Register alerts */ +SELECT dbms_alert.register('a1'); + register +---------- + +(1 row) + +SELECT dbms_alert.register('a2'); + register +---------- + +(1 row) + +SELECT dbms_alert.register('tds'); + register +---------- + +(1 row) + +/* Test: multisession waitone */ +SELECT dbms_alert.waitone('a1',20); + waitone +------------------- + ("Msg1 for a1",0) +(1 row) + +/* Test: multisession waitany */ +SELECT dbms_alert.waitany(10); + waitany +---------------------- + (a2,"Msg1 for a2",0) +(1 row) + +/* Test defered_signal */ +/* This indicated that the transaction has begun */ +SELECT dbms_alert.waitone('tds',10); + waitone +--------------------------------- + ("Begin defered_signal test",0) +(1 row) + +/* The signal will not be received because the transaction is running */ +SELECT dbms_alert.waitone('tds',2); + waitone +--------- + (,1) +(1 row) + +SELECT dbms_alert.signal('b1','Transaction still running'); + signal +-------- + +(1 row) + +SELECT dbms_alert.signal('b1','Transaction committed'); + signal +-------- + +(1 row) + +/* Since the transaction has commited, the signal will be received */ +SELECT dbms_alert.waitone('tds',10); + waitone +------------------------------ + ("Testing defered_signal",0) +(1 row) + +/* Signal session A to send msg1 for a3 */ +SELECT dbms_alert.signal('b2','to check unregistered alert wait'); + signal +-------- + +(1 row) + +/* Test: wait for unregistered alert which is signaled*/ +SELECT dbms_alert.waitone('a3',2); + waitone +--------- + (,1) +(1 row) + +/* Test: Register after alert is signaled and wait */ +SELECT dbms_alert.register('a4'); + register +---------- + +(1 row) + +SELECT dbms_alert.waitone('a4',2); + waitone +--------- + (,1) +(1 row) + +/* Test: remove one */ +SELECT dbms_alert.remove('a1'); + remove +-------- + +(1 row) + +/* Signal session A to send msg2 for a1 */ +SELECT dbms_alert.signal('b3','remove(a1) called'); + signal +-------- + +(1 row) + +/* Test: wait for removed alert */ +SELECT dbms_alert.waitone('a1',2); + waitone +--------- + (,1) +(1 row) + +/* Signal session A to send msg1 for a4 */ +SELECT dbms_alert.signal('b4','to check unremoved alert'); + signal +-------- + +(1 row) + +/* Test: Check if unremoved alert is received */ +SELECT dbms_alert.waitone('a4',10); + waitone +------------------- + ("Msg1 for a4",0) +(1 row) + +/* Test removeall */ +SELECT dbms_alert.removeall(); + removeall +----------- + +(1 row) + +/* Signal session A to send msg2 for a2 */ +SELECT dbms_alert.signal('b5','removeall called'); + signal +-------- + +(1 row) + +/* Test: Use waitany to see if any alert is received */ +SELECT dbms_alert.waitany(2); + waitany +--------- + (,,1) +(1 row) + diff --git a/src/postgres/third-party-extensions/orafce/expected/dbms_alert_session_C.out b/src/postgres/third-party-extensions/orafce/expected/dbms_alert_session_C.out new file mode 100644 index 000000000000..61d8d39d57ca --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/expected/dbms_alert_session_C.out @@ -0,0 +1,35 @@ +\set ECHO all +/* Register alerts */ +SELECT dbms_alert.register('a1'); + register +---------- + +(1 row) + +SELECT dbms_alert.register('a2'); + register +---------- + +(1 row) + +/* Test: multisession waitone */ +SELECT dbms_alert.waitone('a1',20); + waitone +------------------- + ("Msg1 for a1",0) +(1 row) + +/* Test: multisession waitany */ +SELECT dbms_alert.waitany(10); + waitany +---------------------- + (a2,"Msg1 for a2",0) +(1 row) + +/* cleanup */ +SELECT dbms_alert.removeall(); + removeall +----------- + +(1 row) + diff --git a/src/postgres/third-party-extensions/orafce/expected/init.out b/src/postgres/third-party-extensions/orafce/expected/init.out new file mode 100644 index 000000000000..25fdbb1a5c2b --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/expected/init.out @@ -0,0 +1 @@ +\set ECHO none diff --git a/src/postgres/third-party-extensions/orafce/expected/nlssort.out b/src/postgres/third-party-extensions/orafce/expected/nlssort.out new file mode 100644 index 000000000000..9547cc3cbe46 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/expected/nlssort.out @@ -0,0 +1,59 @@ +-- Tests for nlssort +\set ECHO none + name +-------- + brown + Purple + red + yellow +(4 rows) + + name +-------- + Purple + brown + red + yellow +(4 rows) + + set_nls_sort +-------------- + +(1 row) + +ERROR: failed to set the requested LC_COLLATE value [invalid] +CONTEXT: SQL function "nlssort" statement 1 + set_nls_sort +-------------- + +(1 row) + + name +-------- + Purple + brown + red + yellow +(4 rows) + + set_nls_sort +-------------- + +(1 row) + + name +-------- + brown + Purple + red + yellow +(4 rows) + + name +-------- + brown + Purple + red + yellow + +(5 rows) diff --git a/src/postgres/third-party-extensions/orafce/file.c b/src/postgres/third-party-extensions/orafce/file.c new file mode 100644 index 000000000000..f721414ab79b --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/file.c @@ -0,0 +1,1190 @@ +/* + * Attention - this functionality doesn't work when Orace is not linked with + * correct runtime library. The combination "vcruntime140.dll" is working for + * PostgreSQL 12 (vcruntime140d.dll doesn't work). Probably this runtime should + * be same like Postgres server runtime (what is used can be detected by + * dependency walker). Without correct linking the server crash when IO related + * functionality is used. + */ + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#include "postgres.h" + +#include +#include +#include + +#include "executor/spi.h" + +#include "access/htup_details.h" +#include "catalog/pg_type.h" +#include "fmgr.h" +#include "funcapi.h" +#include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "port.h" +#include "storage/fd.h" +#include "utils/builtins.h" +#include "utils/memutils.h" +#include "orafce.h" +#include "builtins.h" + +#ifndef ERRCODE_NO_DATA_FOUND +#define ERRCODE_NO_DATA_FOUND MAKE_SQLSTATE('P','0', '0','0','2') +#endif + +#define INVALID_OPERATION "UTL_FILE_INVALID_OPERATION" +#define WRITE_ERROR "UTL_FILE_WRITE_ERROR" +#define READ_ERROR "UTL_FILE_READ_ERROR" +#define INVALID_FILEHANDLE "UTL_FILE_INVALID_FILEHANDLE" +#define INVALID_MAXLINESIZE "UTL_FILE_INVALID_MAXLINESIZE" +#define INVALID_MODE "UTL_FILE_INVALID_MODE" +#define INVALID_PATH "UTL_FILE_INVALID_PATH" +#define VALUE_ERROR "UTL_FILE_VALUE_ERROR" + +PG_FUNCTION_INFO_V1(utl_file_fopen); +PG_FUNCTION_INFO_V1(utl_file_is_open); +PG_FUNCTION_INFO_V1(utl_file_get_line); +PG_FUNCTION_INFO_V1(utl_file_get_nextline); +PG_FUNCTION_INFO_V1(utl_file_put); +PG_FUNCTION_INFO_V1(utl_file_put_line); +PG_FUNCTION_INFO_V1(utl_file_new_line); +PG_FUNCTION_INFO_V1(utl_file_putf); +PG_FUNCTION_INFO_V1(utl_file_fflush); +PG_FUNCTION_INFO_V1(utl_file_fclose); +PG_FUNCTION_INFO_V1(utl_file_fclose_all); +PG_FUNCTION_INFO_V1(utl_file_fremove); +PG_FUNCTION_INFO_V1(utl_file_frename); +PG_FUNCTION_INFO_V1(utl_file_fcopy); +PG_FUNCTION_INFO_V1(utl_file_fgetattr); +PG_FUNCTION_INFO_V1(utl_file_tmpdir); + +#define CUSTOM_EXCEPTION(msg, detail) \ + ereport(ERROR, \ + (errcode(ERRCODE_RAISE_EXCEPTION), \ + errmsg("%s", msg), \ + errdetail("%s", detail))) + +#define STRERROR_EXCEPTION(msg) \ + do { char *strerr = strerror(errno); CUSTOM_EXCEPTION(msg, strerr); } while(0); + +#define INVALID_FILEHANDLE_EXCEPTION() CUSTOM_EXCEPTION(INVALID_FILEHANDLE, "Used file handle isn't valid.") + +#define CHECK_FILE_HANDLE() \ + if (PG_ARGISNULL(0)) \ + CUSTOM_EXCEPTION(INVALID_FILEHANDLE, "Used file handle isn't valid.") + +#define NON_EMPTY_TEXT(dat) \ + if (VARSIZE(dat) - VARHDRSZ == 0) \ + ereport(ERROR, \ + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), \ + errmsg("invalid parameter"), \ + errdetail("Empty string isn't allowed."))); + +#define NOT_NULL_ARG(n) \ + if (PG_ARGISNULL(n)) \ + ereport(ERROR, \ + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), \ + errmsg("null value not allowed"), \ + errhint("%dth argument is NULL.", n))); + +#define MAX_LINESIZE 32767 + +#define CHECK_LINESIZE(max_linesize) \ + do { \ + if ((max_linesize) < 1 || (max_linesize) > MAX_LINESIZE) \ + CUSTOM_EXCEPTION(INVALID_MAXLINESIZE, "maxlinesize is out of range"); \ + } while(0) + +typedef struct FileSlot +{ + FILE *file; + int max_linesize; + int encoding; + int32 id; +} FileSlot; + +#define MAX_SLOTS 50 /* Oracle 10g supports 50 files */ +#define INVALID_SLOTID 0 /* invalid slot id */ + +static FileSlot slots[MAX_SLOTS]; /* initilaized with zeros */ +static int32 slotid = 0; /* next slot id */ + +static void check_secure_locality(const char *path); +static char *get_safe_path(text *location, text *filename); +static int copy_text_file(FILE *srcfile, FILE *dstfile, + int start_line, int end_line); + +/* + * get_descriptor(FILE *file) find any free slot for FILE pointer. + * If isn't realloc array slots and add 32 new free slots. + * + */ +static int +get_descriptor(FILE *file, int max_linesize, int encoding) +{ + int i; + + for (i = 0; i < MAX_SLOTS; i++) + { + if (slots[i].id == INVALID_SLOTID) + { + slots[i].id = ++slotid; + if (slots[i].id == INVALID_SLOTID) + slots[i].id = ++slotid; /* skip INVALID_SLOTID */ + slots[i].file = file; + slots[i].max_linesize = max_linesize; + slots[i].encoding = encoding; + return slots[i].id; + } + } + + return INVALID_SLOTID; +} + +/* return stored pointer to FILE */ +static FILE * +get_stream(int d, size_t *max_linesize, int *encoding) +{ + int i; + + if (d == INVALID_SLOTID) + INVALID_FILEHANDLE_EXCEPTION(); + + for (i = 0; i < MAX_SLOTS; i++) + { + if (slots[i].id == d) + { + if (max_linesize) + *max_linesize = slots[i].max_linesize; + if (encoding) + *encoding = slots[i].encoding; + return slots[i].file; + } + } + + INVALID_FILEHANDLE_EXCEPTION(); + return NULL; /* keep compiler quiet */ +} + +static void +IO_EXCEPTION(void) +{ + switch (errno) + { + case EACCES: + case ENAMETOOLONG: + case ENOENT: + case ENOTDIR: + STRERROR_EXCEPTION(INVALID_PATH); + break; + + default: + STRERROR_EXCEPTION(INVALID_OPERATION); + } +} + +/* + * FUNCTION UTL_FILE.FOPEN(location text, + * filename text, + * open_mode text, + * max_linesize integer) + * RETURNS UTL_FILE.FILE_TYPE; + * + * The FOPEN function opens specified file and returns file handle. + * open_mode: ['R', 'W', 'A'] + * max_linesize: [1 .. 32767] + * + * Exceptions: + * INVALID_MODE, INVALID_OPERATION, INVALID_PATH, INVALID_MAXLINESIZE + */ +Datum +utl_file_fopen(PG_FUNCTION_ARGS) +{ + text *open_mode; + int max_linesize; + int encoding; + const char *mode = NULL; + FILE *file; + char *fullname; + int d; + + NOT_NULL_ARG(0); + NOT_NULL_ARG(1); + NOT_NULL_ARG(2); + NOT_NULL_ARG(3); + + open_mode = PG_GETARG_TEXT_P(2); + + NON_EMPTY_TEXT(open_mode); + + max_linesize = PG_GETARG_INT32(3); + CHECK_LINESIZE(max_linesize); + + if (PG_NARGS() > 4 && !PG_ARGISNULL(4)) + { + const char *encname = NameStr(*PG_GETARG_NAME(4)); + encoding = pg_char_to_encoding(encname); + if (encoding < 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid encoding name \"%s\"", encname))); + } + else + encoding = GetDatabaseEncoding(); + + if (VARSIZE(open_mode) - VARHDRSZ != 1) + CUSTOM_EXCEPTION(INVALID_MODE, "open mode is different than [R,W,A]"); + + switch (*((char*)VARDATA(open_mode))) + { + case 'a': + case 'A': + mode = "a"; + break; + + case 'r': + case 'R': + mode = "r"; + break; + + case 'w': + case 'W': + mode = "w"; + break; + + default: + CUSTOM_EXCEPTION(INVALID_MODE, "open mode is different than [R,W,A]"); + } + + /* open file */ + fullname = get_safe_path(PG_GETARG_TEXT_P(0), PG_GETARG_TEXT_P(1)); + + /* + * We cannot use AllocateFile here because those files are automatically + * closed at the end of (sub)transactions, but we want to keep them open + * for oracle compatibility. + */ +#if NOT_USED + fullname = convert_encoding_server_to_platform(fullname); +#endif + file = fopen(fullname, mode); + if (!file) + IO_EXCEPTION(); + + d = get_descriptor(file, max_linesize, encoding); + if (d == INVALID_SLOTID) + { + fclose(file); + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("program limit exceeded"), + errdetail("Too much concurent opened files"), + errhint("You can only open a maximum of ten files for each session"))); + } + + PG_RETURN_INT32(d); +} + +Datum +utl_file_is_open(PG_FUNCTION_ARGS) +{ + if (!PG_ARGISNULL(0)) + { + int i; + int d = PG_GETARG_INT32(0); + + for (i = 0; i < MAX_SLOTS; i++) + { + if (slots[i].id == d) + PG_RETURN_BOOL(slots[i].file != NULL); + } + } + + PG_RETURN_BOOL(false); +} + +#define CHECK_LENGTH(l) \ + if (l > max_linesize) \ + CUSTOM_EXCEPTION(VALUE_ERROR, "buffer is too short"); + +/* read line from file. set eof if is EOF */ + +static text * +get_line(FILE *f, size_t max_linesize, int encoding, bool *iseof) +{ + int c; + char *buffer = NULL; + char *bpt; + size_t csize = 0; + text *result = NULL; + bool eof = true; + + buffer = palloc(max_linesize + 2); + bpt = buffer; + + errno = 0; + + while (csize < max_linesize && (c = fgetc(f)) != EOF) + { + eof = false; /* I was able read one char */ + + if (c == '\r') /* lookin ahead \n */ + { + c = fgetc(f); + if (c == EOF) + break; /* last char */ + + if (c != '\n') + ungetc(c, f); + /* skip \r\n */ + break; + } + else if (c == '\n') + break; + + ++csize; + *bpt++ = c; + } + + if (!eof) + { + char *decoded; + size_t len; + + pg_verify_mbstr(encoding, buffer, size2int(csize), false); + decoded = (char *) pg_do_encoding_conversion((unsigned char *) buffer, + size2int(csize), encoding, GetDatabaseEncoding()); + len = (decoded == buffer ? csize : strlen(decoded)); + result = palloc(len + VARHDRSZ); + memcpy(VARDATA(result), decoded, len); + SET_VARSIZE(result, len + VARHDRSZ); + if (decoded != buffer) + pfree(decoded); + *iseof = false; + } + else + { + switch (errno) + { + case 0: + break; + + case EBADF: + CUSTOM_EXCEPTION(INVALID_OPERATION, "file descriptor isn't valid for reading"); + break; + + default: + STRERROR_EXCEPTION(READ_ERROR); + break; + } + + *iseof = true; + } + + pfree(buffer); + return result; +} + + +/* + * FUNCTION UTL_FILE.GET_LINE(file UTL_TYPE.FILE_TYPE, line int DEFAULT NULL) + * RETURNS text; + * + * Reads one line from file. + * + * Exceptions: + * NO_DATA_FOUND, INVALID_FILEHANDLE, INVALID_OPERATION, READ_ERROR + */ +Datum +utl_file_get_line(PG_FUNCTION_ARGS) +{ + size_t max_linesize = 0; /* keep compiler quiet */ + int encoding = 0; /* keep compiler quiet */ + FILE *f; + text *result; + bool iseof; + + CHECK_FILE_HANDLE(); + f = get_stream(PG_GETARG_INT32(0), &max_linesize, &encoding); + + /* 'len' overwrites max_linesize, but must be smaller than max_linesize */ + if (PG_NARGS() > 1 && !PG_ARGISNULL(1)) + { + size_t len = (size_t) PG_GETARG_INT32(1); + CHECK_LINESIZE(len); + if (max_linesize > len) + max_linesize = len; + } + + result = get_line(f, max_linesize, encoding, &iseof); + + if (iseof) + ereport(ERROR, + (errcode(ERRCODE_NO_DATA_FOUND), + errmsg("no data found"))); + + PG_RETURN_TEXT_P(result); +} + + +/* + * FUNCTION UTL_FILE.GET_NEXTLINE(file UTL_TYPE.FILE_TYPE) + * RETURNS text; + * + * Reads one line from file or retutns NULL + * by Steven Feuerstein. + * + * Exceptions: + * INVALID_FILEHANDLE, INVALID_OPERATION, READ_ERROR + */ +Datum +utl_file_get_nextline(PG_FUNCTION_ARGS) +{ + size_t max_linesize = 0; /* keep compiler quiet */ + int encoding = 0; /* keep compiler quiet */ + FILE *f; + text *result; + bool iseof; + + CHECK_FILE_HANDLE(); + f = get_stream(PG_GETARG_INT32(0), &max_linesize, &encoding); + + result = get_line(f, max_linesize, encoding, &iseof); + + if (iseof) + PG_RETURN_NULL(); + + PG_RETURN_TEXT_P(result); +} + +static void +do_flush(FILE *f) +{ + if (fflush(f) != 0) + { + if (errno == EBADF) + CUSTOM_EXCEPTION(INVALID_OPERATION, "File is not an opened, or is not open for writing"); + else + STRERROR_EXCEPTION(WRITE_ERROR); + } +} + +/* + * FUNCTION UTL_FILE.PUT(file UTL_FILE.FILE_TYPE, buffer text) + * RETURNS bool; + * + * The PUT function puts data out to specified file. Buffer length allowed is + * 32K or 1024 (max_linesize); + * + * Exceptions: + * INVALID_FILEHANDLE, INVALID_OPERATION, WRITE_ERROR, VALUE_ERROR + * + * Note: returns bool because I cannot do envelope over void function + */ + +#define CHECK_ERRNO_PUT() \ + switch (errno) \ + { \ + case EBADF: \ + CUSTOM_EXCEPTION(INVALID_OPERATION, "file descriptor isn't valid for writing"); \ + break; \ + default: \ + STRERROR_EXCEPTION(WRITE_ERROR); \ + } + +/* encode(t, encoding) */ +static char * +encode_text(int encoding, text *t, size_t *length) +{ + char *src = VARDATA_ANY(t); + char *encoded; + + encoded = (char *) pg_do_encoding_conversion((unsigned char *) src, + VARSIZE_ANY_EXHDR(t), GetDatabaseEncoding(), encoding); + + *length = (src == encoded ? VARSIZE_ANY_EXHDR(t) : strlen(encoded)); + return encoded; +} + +/* fwrite(encode(args[n], encoding), f) */ +static size_t +do_write(PG_FUNCTION_ARGS, int n, FILE *f, size_t max_linesize, int encoding) +{ + text *arg = PG_GETARG_TEXT_P(n); + char *str; + size_t len; + + str = encode_text(encoding, arg, &len); + CHECK_LENGTH(len); + + if (fwrite(str, 1, len, f) != len) + CHECK_ERRNO_PUT(); + + if (VARDATA(arg) != str) + pfree(str); + PG_FREE_IF_COPY(arg, n); + + return len; +} + +static FILE * +do_put(PG_FUNCTION_ARGS) +{ + FILE *f; + size_t max_linesize = 0; /* keep compiler quiet */ + int encoding = 0; /* keep compiler quiet */ + + CHECK_FILE_HANDLE(); + f = get_stream(PG_GETARG_INT32(0), &max_linesize, &encoding); + + NOT_NULL_ARG(1); + do_write(fcinfo, 1, f, max_linesize, encoding); + return f; +} + +Datum +utl_file_put(PG_FUNCTION_ARGS) +{ + do_put(fcinfo); + PG_RETURN_BOOL(true); +} + +static void +do_new_line(FILE *f, int lines) +{ + int i; + for (i = 0; i < lines; i++) + { +#ifndef WIN32 + if (fputc('\n', f) == EOF) + CHECK_ERRNO_PUT(); +#else + if (fputs("\r\n", f) == EOF) + CHECK_ERRNO_PUT(); +#endif + } +} + +Datum +utl_file_put_line(PG_FUNCTION_ARGS) +{ + FILE *f; + bool autoflush; + + f = do_put(fcinfo); + + autoflush = PG_GETARG_IF_EXISTS(2, BOOL, false); + + do_new_line(f, 1); + + if (autoflush) + do_flush(f); + + PG_RETURN_BOOL(true); +} + +Datum +utl_file_new_line(PG_FUNCTION_ARGS) +{ + FILE *f; + int lines; + + CHECK_FILE_HANDLE(); + f = get_stream(PG_GETARG_INT32(0), NULL, NULL); + lines = PG_GETARG_IF_EXISTS(1, INT32, 1); + + do_new_line(f, lines); + + PG_RETURN_BOOL(true); +} + +/* + * FUNCTION UTL_FILE.PUTF(file UTL_FILE.FILE_TYPE, + * format text, + * arg1 text, + * arg2 text, + * arg3 text, + * arg4 text, + * arg5 text) + * RETURNS bool; + * + * Puts formated data to file. Allows %s like subst symbol. + * + * Exception: + * INVALID_FILEHANDLE, INVALID_OPERATION, WRITE_ERROR + */ +Datum +utl_file_putf(PG_FUNCTION_ARGS) +{ + FILE *f; + char *format; + size_t max_linesize; + int encoding; + size_t format_length; + char *fpt; + int cur_par = 0; + size_t cur_len = 0; + + CHECK_FILE_HANDLE(); + f = get_stream(PG_GETARG_INT32(0), &max_linesize, &encoding); + + NOT_NULL_ARG(1); + format = encode_text(encoding, PG_GETARG_TEXT_P(1), &format_length); + + for (fpt = format; format_length > 0; fpt++, format_length--) + { + if (format_length == 1) + { + /* last char */ + CHECK_LENGTH(++cur_len); + if (fputc(*fpt, f) == EOF) + CHECK_ERRNO_PUT(); + continue; + } + /* ansi compatible string */ + if (fpt[0] == '\\' && fpt[1] == 'n') + { + CHECK_LENGTH(++cur_len); + if (fputc('\n', f) == EOF) + CHECK_ERRNO_PUT(); + fpt++; format_length--; + continue; + } + if (fpt[0] == '%') + { + if (fpt[1] == '%') + { + CHECK_LENGTH(++cur_len); + if (fputc('%', f) == EOF) + CHECK_ERRNO_PUT(); + } + else if (fpt[1] == 's' && ++cur_par <= 5 && !PG_ARGISNULL(cur_par + 1)) + { + cur_len += do_write(fcinfo, cur_par + 1, f, max_linesize - cur_len, encoding); + } + fpt++; format_length--; + continue; + } + CHECK_LENGTH(++cur_len); + if (fputc(fpt[0], f) == EOF) + CHECK_ERRNO_PUT(); + } + + PG_RETURN_BOOL(true); +} + + +/* + * FUNCTION UTL_FILE.FFLUSH(file UTL_FILE.FILE_TYPE) + * RETURNS void; + * + * This function makes sure that all pending data for the specified file is written + * physically out to file. + * + * Exceptions: + * INVALID_FILEHANDLE, INVALID_OPERATION, WRITE_ERROR + */ +Datum +utl_file_fflush(PG_FUNCTION_ARGS) +{ + FILE *f; + + CHECK_FILE_HANDLE(); + f = get_stream(PG_GETARG_INT32(0), NULL, NULL); + do_flush(f); + + PG_RETURN_VOID(); +} + + +/* + * FUNCTION UTL_FILE.FCLOSE(file UTL_FILE.FILE_TYPE) + * RETURNS NULL + * + * Close an open file. This function reset file handle to NULL on Oracle platform. + * It isn't possible in PostgreSQL, and then you have to call fclose function + * like: + * file := utl_file.fclose(file); + * + * Exception: + * INVALID_FILEHANDLE, WRITE_ERROR + */ +Datum +utl_file_fclose(PG_FUNCTION_ARGS) +{ + int i; + int d = PG_GETARG_INT32(0); + + for (i = 0; i < MAX_SLOTS; i++) + { + if (slots[i].id == d) + { + if (slots[i].file && fclose(slots[i].file) != 0) + { + if (errno == EBADF) + CUSTOM_EXCEPTION(INVALID_FILEHANDLE, "File is not an opened"); + else + STRERROR_EXCEPTION(WRITE_ERROR); + } + slots[i].file = NULL; + slots[i].id = INVALID_SLOTID; + PG_RETURN_NULL(); + } + } + + INVALID_FILEHANDLE_EXCEPTION(); + PG_RETURN_NULL(); +} + + +/* + * FUNCTION UTL_FILE.FCLOSE_ALL() + * RETURNS void + * + * Close all opened files. + * + * Exceptions: WRITE_ERROR + */ +Datum +utl_file_fclose_all(PG_FUNCTION_ARGS) +{ + int i; + + for (i = 0; i < MAX_SLOTS; i++) + { + if (slots[i].id != INVALID_SLOTID) + { + if (slots[i].file && fclose(slots[i].file) != 0) + { + if (errno == EBADF) + CUSTOM_EXCEPTION(INVALID_FILEHANDLE, "File is not an opened"); + else + STRERROR_EXCEPTION(WRITE_ERROR); + } + slots[i].file = NULL; + slots[i].id = INVALID_SLOTID; + } + } + + PG_RETURN_VOID(); +} + + +/* + * utl_file_dir security .. is solved with aux. table. + * + * Raise exception if don't find string in table. + */ +static void +check_secure_locality(const char *path) +{ + static SPIPlanPtr plan = NULL; + + Oid argtypes[] = {TEXTOID}; + Datum values[1]; + char nulls[1] = {' '}; + + values[0] = CStringGetTextDatum(path); + + /* + * SELECT 1 FROM utl_file.utl_file_dir + * WHERE CASE WHEN substring(dir from '.$') = '/' THEN + * substring($1, 1, length(dir)) = dir + * ELSE + * substring($1, 1, length(dir) + 1) = dir || '/' + * END + */ + + if (SPI_connect() < 0) + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("SPI_connect failed"))); + + if (!plan) + { + /* Don't use LIKE not to escape '_' and '%' */ + SPIPlanPtr p = SPI_prepare( + "SELECT 1 FROM utl_file.utl_file_dir" + " WHERE CASE WHEN substring(dir from '.$') = '/' THEN" + " substring($1, 1, length(dir)) = dir" + " ELSE" + " substring($1, 1, length(dir) + 1) = dir || '/'" + " END", + 1, argtypes); + + if (p == NULL || (plan = SPI_saveplan(p)) == NULL) + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("SPI_prepare_failed"))); + } + + if (SPI_OK_SELECT != SPI_execute_plan(plan, values, nulls, false, 1)) + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("can't execute sql"))); + + if (SPI_processed == 0) + ereport(ERROR, + (errcode(ERRCODE_RAISE_EXCEPTION), + errmsg(INVALID_PATH), + errdetail("you cannot access locality"), + errhint("locality is not found in utl_file_dir table"))); + SPI_finish(); +} + +static char * +safe_named_location(text *location) +{ + static SPIPlanPtr plan = NULL; + MemoryContext old_cxt; + + Oid argtypes[] = {TEXTOID}; + Datum values[1]; + char nulls[1] = {' '}; + char *result; + + old_cxt = CurrentMemoryContext; + + values[0] = PointerGetDatum(location); + + if (SPI_connect() < 0) + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("SPI_connect failed"))); + + if (!plan) + { + /* Don't use LIKE not to escape '_' and '%' */ + SPIPlanPtr p = SPI_prepare( + "SELECT dir FROM utl_file.utl_file_dir WHERE dirname = $1", + 1, argtypes); + + if (p == NULL || (plan = SPI_saveplan(p)) == NULL) + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("SPI_prepare_failed"))); + } + + if (SPI_OK_SELECT != SPI_execute_plan(plan, values, nulls, false, 1)) + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("can't execute sql"))); + + if (SPI_processed > 0) + { + char *loc = SPI_getvalue(SPI_tuptable->vals[0], + SPI_tuptable->tupdesc, 1); + if (loc) + result = MemoryContextStrdup(old_cxt, loc); + else + result = NULL; + } + else + result = NULL; + + SPI_finish(); + + MemoryContextSwitchTo(old_cxt); + + return result; +} + + +/* + * get_safe_path - make a fullpath and check security. + */ +static char * +get_safe_path(text *location_or_dirname, text *filename) +{ + char *fullname; + char *location; + bool check_locality; + + NON_EMPTY_TEXT(location_or_dirname); + NON_EMPTY_TEXT(filename); + + location = safe_named_location(location_or_dirname); + if (location) + { + int aux_pos = size2int(strlen(location)); + int aux_len = VARSIZE_ANY_EXHDR(filename); + + fullname = palloc(aux_pos + 1 + aux_len + 1); + strcpy(fullname, location); + fullname[aux_pos] = '/'; + memcpy(fullname + aux_pos + 1, VARDATA(filename), aux_len); + fullname[aux_pos + aux_len + 1] = '\0'; + + /* location is safe (ensured by dirname) */ + check_locality = false; + pfree(location); + } + else + { + int aux_pos = VARSIZE_ANY_EXHDR(location_or_dirname); + int aux_len = VARSIZE_ANY_EXHDR(filename); + + fullname = palloc(aux_pos + 1 + aux_len + 1); + memcpy(fullname, VARDATA(location_or_dirname), aux_pos); + fullname[aux_pos] = '/'; + memcpy(fullname + aux_pos + 1, VARDATA(filename), aux_len); + fullname[aux_pos + aux_len + 1] = '\0'; + + check_locality = true; + } + + /* check locality in canonizalized form of path */ + canonicalize_path(fullname); + + if (check_locality) + check_secure_locality(fullname); + + return fullname; +} + +/* + * CREATE FUNCTION utl_file.fremove( + * location text, + * filename text) + */ +Datum +utl_file_fremove(PG_FUNCTION_ARGS) +{ + char *fullname; + + NOT_NULL_ARG(0); + NOT_NULL_ARG(1); + + fullname = get_safe_path(PG_GETARG_TEXT_P(0), PG_GETARG_TEXT_P(1)); + + if (unlink(fullname) != 0) + IO_EXCEPTION(); + + PG_RETURN_VOID(); +} + +/* + * CREATE FUNCTION utl_file.frename( + * location text, + * filename text, + * dest_dir text, + * dest_file text, + * overwrite boolean DEFAULT false) + */ +Datum +utl_file_frename(PG_FUNCTION_ARGS) +{ + char *srcpath; + char *dstpath; + bool overwrite; + + NOT_NULL_ARG(0); + NOT_NULL_ARG(1); + NOT_NULL_ARG(2); + NOT_NULL_ARG(3); + + overwrite = PG_GETARG_IF_EXISTS(4, BOOL, false); + srcpath = get_safe_path(PG_GETARG_TEXT_P(0), PG_GETARG_TEXT_P(1)); + dstpath = get_safe_path(PG_GETARG_TEXT_P(2), PG_GETARG_TEXT_P(3)); + + if (!overwrite) + { + struct stat st; + if (stat(dstpath, &st) == 0) + CUSTOM_EXCEPTION(WRITE_ERROR, "File exists"); + else if (errno != ENOENT) + IO_EXCEPTION(); + } + + /* rename() overwrites existing files. */ + if (rename(srcpath, dstpath) != 0) + IO_EXCEPTION(); + + PG_RETURN_VOID(); +} + +/* + * CREATE FUNCTION utl_file.fcopy( + * src_location text, + * src_filename text, + * dest_location text, + * dest_filename text, + * start_line integer DEFAULT NULL + * end_line integer DEFAULT NULL) + */ +Datum +utl_file_fcopy(PG_FUNCTION_ARGS) +{ + char *srcpath; + char *dstpath; + int start_line; + int end_line; + FILE *srcfile; + FILE *dstfile; + + NOT_NULL_ARG(0); + NOT_NULL_ARG(1); + NOT_NULL_ARG(2); + NOT_NULL_ARG(3); + + srcpath = get_safe_path(PG_GETARG_TEXT_P(0), PG_GETARG_TEXT_P(1)); + dstpath = get_safe_path(PG_GETARG_TEXT_P(2), PG_GETARG_TEXT_P(3)); + + start_line = PG_GETARG_IF_EXISTS(4, INT32, 1); + if (start_line <= 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("start_line must be positive (%d passed)", start_line))); + + end_line = PG_GETARG_IF_EXISTS(5, INT32, INT_MAX); + if (end_line <= 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("end_line must be positive (%d passed)", end_line))); + + srcfile = AllocateFile(srcpath, "rt"); + if (srcfile == NULL) + { + /* failed to open src file. */ + IO_EXCEPTION(); + } + + dstfile = AllocateFile(dstpath, "wt"); + if (dstfile == NULL) + { + /* failed to open dst file. */ + fclose(srcfile); + IO_EXCEPTION(); + } + + if (copy_text_file(srcfile, dstfile, start_line, end_line)) + IO_EXCEPTION(); + + FreeFile(srcfile); + FreeFile(dstfile); + + PG_RETURN_VOID(); +} + +/* + * Copy srcfile to dstfile. Return 0 if succeeded, or non-0 if error. + */ +static int +copy_text_file(FILE *srcfile, FILE *dstfile, int start_line, int end_line) +{ + char *buffer; + size_t len; + int i; + + buffer = palloc(MAX_LINESIZE); + + errno = 0; + + /* skip first start_line. */ + for (i = 1; i < start_line; i++) + { + CHECK_FOR_INTERRUPTS(); + do + { + if (fgets(buffer, MAX_LINESIZE, srcfile) == NULL) + return errno; + len = strlen(buffer); + } while(buffer[len - 1] != '\n'); + } + + /* copy until end_line. */ + for (; i <= end_line; i++) + { + CHECK_FOR_INTERRUPTS(); + do + { + if (fgets(buffer, MAX_LINESIZE, srcfile) == NULL) + return errno; + len = strlen(buffer); + if (fwrite(buffer, 1, len, dstfile) != len) + return errno; + } while(buffer[len - 1] != '\n'); + } + + pfree(buffer); + + return 0; +} + +/* + * CREATE FUNCTION utl_file.fgetattr( + * location text, + * filename text + * ) RETURNS ( + * fexists boolean, + * file_length bigint, + * blocksize integer) + */ +Datum +utl_file_fgetattr(PG_FUNCTION_ARGS) +{ + char *fullname; + struct stat st; + TupleDesc tupdesc; + Datum result; + HeapTuple tuple; + Datum values[3]; + bool nulls[3] = { 0 }; + + NOT_NULL_ARG(0); + NOT_NULL_ARG(1); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + fullname = get_safe_path(PG_GETARG_TEXT_P(0), PG_GETARG_TEXT_P(1)); + + if (stat(fullname, &st) == 0) + { + values[0] = BoolGetDatum(true); + values[1] = Int64GetDatum(st.st_size); +#ifndef WIN32 + values[2] = Int32GetDatum(st.st_blksize); +#else + values[2] = 512; /* NTFS block size */ +#endif + } + else + { + values[0] = BoolGetDatum(false); + nulls[1] = true; + nulls[2] = true; + } + + tuple = heap_form_tuple(tupdesc, values, nulls); + result = HeapTupleGetDatum(tuple); + + PG_RETURN_DATUM(result); +} + +Datum +utl_file_tmpdir(PG_FUNCTION_ARGS) +{ +#ifndef WIN32 + const char *tmpdir = getenv("TMPDIR"); + + if (!tmpdir) + tmpdir = "/tmp"; +#else + char tmpdir[MAXPGPATH]; + int ret; + + ret = GetTempPathA(MAXPGPATH, tmpdir); + if (ret == 0 || ret > MAXPGPATH) + CUSTOM_EXCEPTION(INVALID_PATH, strerror(errno)); + + canonicalize_path(tmpdir); +#endif + + PG_RETURN_TEXT_P(cstring_to_text(tmpdir)); +} diff --git a/src/postgres/third-party-extensions/orafce/magic.c b/src/postgres/third-party-extensions/orafce/magic.c new file mode 100644 index 000000000000..acc21a4569e9 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/magic.c @@ -0,0 +1,6 @@ +#include "postgres.h" +#include "fmgr.h" + +#ifdef PG_MODULE_MAGIC +PG_MODULE_MAGIC; +#endif diff --git a/src/postgres/third-party-extensions/orafce/nvarchar2.c b/src/postgres/third-party-extensions/orafce/nvarchar2.c new file mode 100644 index 000000000000..81037acdbf8e --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/nvarchar2.c @@ -0,0 +1,199 @@ +/*---------------------------------------------------------------------------- + * + * nvarchar2.c + * NVARCHAR2 type for PostgreSQL. + * + *---------------------------------------------------------------------------- + */ + +#include "postgres.h" +#include "access/hash.h" +#include "libpq/pqformat.h" +#include "nodes/nodeFuncs.h" +#include "utils/array.h" +#include "utils/builtins.h" +#include "mb/pg_wchar.h" +#include "fmgr.h" + +#include "orafce.h" +#include "builtins.h" + + +PG_FUNCTION_INFO_V1(nvarchar2in); +PG_FUNCTION_INFO_V1(nvarchar2out); +PG_FUNCTION_INFO_V1(nvarchar2); +PG_FUNCTION_INFO_V1(nvarchar2recv); + +/* + * nvarchar2_input -- common guts of nvarchar2in and nvarchar2recv + * + * s is the input text of length len (may not be null-terminated) + * atttypmod is the typmod value to apply + * + * If the input string is too long, raise an error + * + * Uses the C string to text conversion function, which is only appropriate + * if VarChar and text are equivalent types. + */ + +static VarChar * +nvarchar2_input(const char *s, size_t len, int32 atttypmod) +{ + VarChar *result; /* input data */ + size_t maxlen; + + maxlen = atttypmod - VARHDRSZ; + + /* + * Perform the typmod check; error out if value too long for NVARCHAR2 + */ + if (atttypmod >= (int32) VARHDRSZ && len > maxlen) + { + /* Verify that input length is within typmod limit. + * + * NOTE: blankspace is not truncated + */ + size_t mbmaxlen = pg_mbstrlen(s); + + if (mbmaxlen > maxlen) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("input value length is %zd; too long for type nvarchar2(%zd)", mbmaxlen , maxlen))); + } + + result = (VarChar *) cstring_to_text_with_len(s, size2int(len)); + return result; +} + +/* + * Converts a C string to NVARCHAR2 internal representation. atttypmod + * is the declared length of the type plus VARHDRSZ. + */ +Datum +nvarchar2in(PG_FUNCTION_ARGS) +{ + char *s = PG_GETARG_CSTRING(0); +#ifdef NOT_USED + Oid typelem = PG_GETARG_OID(1); +#endif + int32 atttypmod = PG_GETARG_INT32(2); + VarChar *result; + + result = nvarchar2_input(s, strlen(s), atttypmod); + PG_RETURN_VARCHAR_P(result); +} + + +/* + * converts a NVARCHAR2 value to a C string. + * + * Uses the text to C string conversion function, which is only appropriate + * if VarChar and text are equivalent types. + */ +Datum +nvarchar2out(PG_FUNCTION_ARGS) +{ + Datum txt = PG_GETARG_DATUM(0); + + PG_RETURN_CSTRING(TextDatumGetCString(txt)); +} + +/* + * converts external binary format to nvarchar + */ +Datum +nvarchar2recv(PG_FUNCTION_ARGS) +{ + StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); + +#ifdef NOT_USED + Oid typelem = PG_GETARG_OID(1); +#endif + int32 atttypmod = PG_GETARG_INT32(2); /* typmod of the receiving column */ + VarChar *result; + char *str; /* received data */ + int nbytes; /* length in bytes of recived data */ + + str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes); + result = nvarchar2_input(str, nbytes, atttypmod); + pfree(str); + PG_RETURN_VARCHAR_P(result); +} + + +/* + * nvarchar2send -- convert nvarchar2 to binary value + * + * just use varcharsend() + */ + +/* + * nvarchar2_transform() + * Flatten calls to varchar's length coercion function that set the new maximum + * length >= the previous maximum length. We can ignore the isExplicit + * argument, since that only affects truncation cases. + * + * just use varchar_transform() + */ + +/* + * Converts a NVARCHAR2 type to the specified size. + * + * maxlen is the typmod, ie, declared length plus VARHDRSZ bytes. + * isExplicit is true if this is for an explicit cast to nvarchar2(N). + * + * Truncation rules: for an explicit cast, silently truncate to the given + * length; for an implicit cast, raise error if length limit is exceeded + */ +Datum +nvarchar2(PG_FUNCTION_ARGS) +{ + VarChar *source = PG_GETARG_VARCHAR_PP(0); + int32 typmod = PG_GETARG_INT32(1); + bool isExplicit = PG_GETARG_BOOL(2); + int32 len, + maxlen; + int maxmblen; + char *s_data; + + len = VARSIZE_ANY_EXHDR(source); + s_data = VARDATA_ANY(source); + maxlen = typmod - VARHDRSZ; + + /* No work if typmod is invalid or supplied data fits it already */ + if (maxlen < 0 || len <= maxlen) + PG_RETURN_VARCHAR_P(source); + + /* only reach here if string is too long... */ + + /* truncate multibyte string preserving multibyte boundary */ + maxmblen = pg_mbcharcliplen(s_data, len, maxlen); + + /* error out if value too long unless it's an explicit cast */ + if (!isExplicit) + { + /* if there is still data beyond maxmblen, error out + * + * Remember - no blankspace truncation on implicit cast + */ + if (len > maxmblen) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("input value too long for type nvarchar2(%d)", maxlen))); + } + + PG_RETURN_VARCHAR_P((VarChar *) cstring_to_text_with_len(s_data, size2int(maxmblen))); +} + + +/* + * nvarchar2typmodin -- type modifier input function + * + * just use varchartypmodin() + */ + +/* + * nvarchar2typmodout -- type modifier output function + * + * just use varchartypmodout() + */ diff --git a/src/postgres/third-party-extensions/orafce/orafce--3.10--3.11.sql b/src/postgres/third-party-extensions/orafce/orafce--3.10--3.11.sql new file mode 100644 index 000000000000..767104a23ba5 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/orafce--3.10--3.11.sql @@ -0,0 +1,3 @@ +ALTER FUNCTION utl_file.fopen(text, text, text, integer, name) SECURITY INVOKER; +ALTER FUNCTION utl_file.fopen(text, text, text, integer) SECURITY INVOKER; +GRANT SELECT ON TABLE utl_file.utl_file_dir TO PUBLIC; diff --git a/src/postgres/third-party-extensions/orafce/orafce--3.11--3.12.sql b/src/postgres/third-party-extensions/orafce/orafce--3.11--3.12.sql new file mode 100644 index 000000000000..72675c4884cf --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/orafce--3.11--3.12.sql @@ -0,0 +1,9 @@ +CREATE OR REPLACE FUNCTION oracle.replace_empty_strings() +RETURNS TRIGGER +AS 'MODULE_PATHNAME','orafce_replace_empty_strings' +LANGUAGE 'c'; + +CREATE OR REPLACE FUNCTION oracle.replace_null_strings() +RETURNS TRIGGER +AS 'MODULE_PATHNAME','orafce_replace_null_strings' +LANGUAGE 'c'; diff --git a/src/postgres/third-party-extensions/orafce/orafce--3.12--3.13.sql b/src/postgres/third-party-extensions/orafce/orafce--3.12--3.13.sql new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/postgres/third-party-extensions/orafce/orafce--3.13--3.14.sql b/src/postgres/third-party-extensions/orafce/orafce--3.13--3.14.sql new file mode 100644 index 000000000000..ec805051845c --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/orafce--3.13--3.14.sql @@ -0,0 +1,34 @@ +CREATE OR REPLACE FUNCTION oracle.unistr(text) +RETURNS text +AS 'MODULE_PATHNAME','orafce_unistr' +LANGUAGE 'c'; + +do $$ +BEGIN + IF EXISTS(SELECT * FROM pg_settings WHERE name = 'server_version_num' AND setting::int >= 120000) THEN + ALTER FUNCTION varchar2(varchar2, integer, boolean) SUPPORT varchar2_transform; + ELSE + UPDATE pg_proc SET protransform= 'varchar2_transform'::regproc::oid WHERE proname='varchar2'; + + INSERT INTO pg_depend (classid, objid, objsubid, + refclassid, refobjid, refobjsubid, deptype) + VALUES('pg_proc'::regclass::oid, 'varchar2'::regproc::oid, 0, + 'pg_proc'::regclass::oid, 'varchar2_transform'::regproc::oid, 0, 'n'); + END IF; +END +$$; + +do $$ +BEGIN + IF EXISTS(SELECT * FROM pg_settings WHERE name = 'server_version_num' AND setting::int >= 120000) THEN + ALTER FUNCTION nvarchar2(nvarchar2, integer, boolean) SUPPORT nvarchar2_transform; + ELSE + UPDATE pg_proc SET protransform= 'nvarchar2_transform'::regproc::oid WHERE proname='nvarchar2'; + + INSERT INTO pg_depend (classid, objid, objsubid, + refclassid, refobjid, refobjsubid, deptype) + VALUES('pg_proc'::regclass::oid, 'nvarchar2'::regproc::oid, 0, + 'pg_proc'::regclass::oid, 'nvarchar2_transform'::regproc::oid, 0, 'n'); + END IF; +END +$$; diff --git a/src/postgres/third-party-extensions/orafce/orafce--3.14.sql b/src/postgres/third-party-extensions/orafce/orafce--3.14.sql new file mode 100644 index 000000000000..f8737f79dfe3 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/orafce--3.14.sql @@ -0,0 +1,3557 @@ +/* orafce--3.12.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "CREATE EXTENSION orafce" to load this file. \quit + +CREATE FUNCTION pg_catalog.trunc(value date, fmt text) +RETURNS date +AS 'MODULE_PATHNAME','ora_date_trunc' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.trunc(date,text) IS 'truncate date according to the specified format'; + +CREATE FUNCTION pg_catalog.round(value date, fmt text) +RETURNS date +AS 'MODULE_PATHNAME','ora_date_round' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.round(date, text) IS 'round dates according to the specified format'; + +CREATE FUNCTION pg_catalog.next_day(value date, weekday text) +RETURNS date +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.next_day (date, text) IS 'returns the first weekday that is greater than a date value'; + +CREATE FUNCTION pg_catalog.next_day(value date, weekday integer) +RETURNS date +AS 'MODULE_PATHNAME', 'next_day_by_index' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.next_day (date, integer) IS 'returns the first weekday that is greater than a date value'; + +CREATE FUNCTION pg_catalog.last_day(value date) +RETURNS date +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.last_day(date) IS 'returns last day of the month based on a date value'; + +CREATE FUNCTION pg_catalog.months_between(date1 date, date2 date) +RETURNS numeric +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.months_between(date, date) IS 'returns the number of months between date1 and date2'; + +CREATE FUNCTION pg_catalog.add_months(day date, value int) +RETURNS date +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.add_months(date, int) IS 'returns date plus n months'; + +CREATE FUNCTION pg_catalog.trunc(value timestamp with time zone, fmt text) +RETURNS timestamp with time zone +AS 'MODULE_PATHNAME', 'ora_timestamptz_trunc' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.trunc(timestamp with time zone, text) IS 'truncate date according to the specified format'; + +CREATE FUNCTION pg_catalog.round(value timestamp with time zone, fmt text) +RETURNS timestamp with time zone +AS 'MODULE_PATHNAME','ora_timestamptz_round' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.round(timestamp with time zone, text) IS 'round dates according to the specified format'; + +CREATE FUNCTION pg_catalog.round(value timestamp with time zone) +RETURNS timestamp with time zone +AS $$ SELECT pg_catalog.round($1, 'DDD'); $$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.round(timestamp with time zone) IS 'will round dates according to the specified format'; + +CREATE FUNCTION pg_catalog.round(value date) +RETURNS date +AS $$ SELECT $1; $$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.round(value date)IS 'will round dates according to the specified format'; + +CREATE FUNCTION pg_catalog.trunc(value timestamp with time zone) +RETURNS timestamp with time zone +AS $$ SELECT pg_catalog.trunc($1, 'DDD'); $$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.trunc(timestamp with time zone) IS 'truncate date according to the specified format'; + +CREATE FUNCTION pg_catalog.trunc(value date) +RETURNS date +AS $$ SELECT $1; $$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.trunc(date) IS 'truncate date according to the specified format'; + +CREATE FUNCTION pg_catalog.nlssort(text, text) +RETURNS bytea +AS 'MODULE_PATHNAME', 'ora_nlssort' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION pg_catalog.nlssort(text, text) IS ''; + +CREATE FUNCTION pg_catalog.nlssort(text) +RETURNS bytea +AS $$ SELECT pg_catalog.nlssort($1, null); $$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.nlssort(text)IS ''; + +CREATE FUNCTION pg_catalog.set_nls_sort(text) +RETURNS void +AS 'MODULE_PATHNAME', 'ora_set_nls_sort' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.set_nls_sort(text) IS ''; + +CREATE FUNCTION pg_catalog.instr(str text, patt text, start int, nth int) +RETURNS int +AS 'MODULE_PATHNAME','plvstr_instr4' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.instr(text, text, int, int) IS 'Search pattern in string'; + +CREATE FUNCTION pg_catalog.instr(str text, patt text, start int) +RETURNS int +AS 'MODULE_PATHNAME','plvstr_instr3' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.instr(text, text, int) IS 'Search pattern in string'; + +CREATE FUNCTION pg_catalog.instr(str text, patt text) +RETURNS int +AS 'MODULE_PATHNAME','plvstr_instr2' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.instr(text, text) IS 'Search pattern in string'; + +CREATE FUNCTION pg_catalog.to_char(num smallint) +RETURNS text +AS 'MODULE_PATHNAME','orafce_to_char_int4' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.to_char(smallint) IS 'Convert number to string'; + +CREATE FUNCTION pg_catalog.to_char(num int) +RETURNS text +AS 'MODULE_PATHNAME','orafce_to_char_int4' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.to_char(int) IS 'Convert number to string'; + +CREATE FUNCTION pg_catalog.to_char(num bigint) +RETURNS text +AS 'MODULE_PATHNAME','orafce_to_char_int8' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.to_char(bigint) IS 'Convert number to string'; + +CREATE FUNCTION pg_catalog.to_char(num real) +RETURNS text +AS 'MODULE_PATHNAME','orafce_to_char_float4' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.to_char(real) IS 'Convert number to string'; + +CREATE FUNCTION pg_catalog.to_char(num double precision) +RETURNS text +AS 'MODULE_PATHNAME','orafce_to_char_float8' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.to_char(double precision) IS 'Convert number to string'; + +CREATE FUNCTION pg_catalog.to_char(num numeric) +RETURNS text +AS 'MODULE_PATHNAME','orafce_to_char_numeric' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.to_char(numeric) IS 'Convert number to string'; + +CREATE FUNCTION pg_catalog.to_number(str text) +RETURNS numeric +AS 'MODULE_PATHNAME','orafce_to_number' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.to_number(text) IS 'Convert string to number'; + +CREATE OR REPLACE FUNCTION pg_catalog.to_number(numeric) +RETURNS numeric AS $$ +SELECT pg_catalog.to_number($1::text); +$$ LANGUAGE SQL IMMUTABLE; + +CREATE OR REPLACE FUNCTION pg_catalog.to_number(numeric,numeric) +RETURNS numeric AS $$ +SELECT pg_catalog.to_number($1::text,$2::text); +$$ LANGUAGE SQL IMMUTABLE; + +CREATE FUNCTION pg_catalog.to_date(str text) +RETURNS timestamp +AS 'MODULE_PATHNAME','ora_to_date' +LANGUAGE C STABLE STRICT; +COMMENT ON FUNCTION pg_catalog.to_date(text) IS 'Convert string to timestamp'; + +CREATE FUNCTION to_multi_byte(str text) +RETURNS text +AS 'MODULE_PATHNAME','orafce_to_multi_byte' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION to_multi_byte(text) IS 'Convert all single-byte characters to their corresponding multibyte characters'; + +CREATE FUNCTION to_single_byte(str text) +RETURNS text +AS 'MODULE_PATHNAME','orafce_to_single_byte' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION to_single_byte(text) IS 'Convert characters to their corresponding single-byte characters if possible'; + +CREATE FUNCTION bitand(bigint, bigint) +RETURNS bigint +AS $$ SELECT $1 & $2; $$ +LANGUAGE sql IMMUTABLE STRICT; + +CREATE FUNCTION sinh(float8) +RETURNS float8 AS +$$ SELECT (exp($1) - exp(-$1)) / 2; $$ +LANGUAGE sql IMMUTABLE STRICT; + +CREATE FUNCTION cosh(float8) +RETURNS float8 AS +$$ SELECT (exp($1) + exp(-$1)) / 2; $$ +LANGUAGE sql IMMUTABLE STRICT; + +CREATE FUNCTION tanh(float8) +RETURNS float8 AS +$$ SELECT sinh($1) / cosh($1); $$ +LANGUAGE sql IMMUTABLE STRICT; + +CREATE FUNCTION nanvl(float4, float4) +RETURNS float4 AS +$$ SELECT CASE WHEN $1 = 'NaN' THEN $2 ELSE $1 END; $$ +LANGUAGE sql IMMUTABLE STRICT; + +CREATE FUNCTION nanvl(float8, float8) +RETURNS float8 AS +$$ SELECT CASE WHEN $1 = 'NaN' THEN $2 ELSE $1 END; $$ +LANGUAGE sql IMMUTABLE STRICT; + +CREATE FUNCTION nanvl(numeric, numeric) +RETURNS numeric AS +$$ SELECT CASE WHEN $1 = 'NaN' THEN $2 ELSE $1 END; $$ +LANGUAGE sql IMMUTABLE STRICT; + +CREATE FUNCTION nanvl(float4, varchar) +RETURNS float4 AS +$$ SELECT CASE WHEN $1 = 'NaN' THEN $2::float4 ELSE $1 END; $$ +LANGUAGE sql IMMUTABLE STRICT; + +CREATE FUNCTION nanvl(float8, varchar) +RETURNS float8 AS +$$ SELECT CASE WHEN $1 = 'NaN' THEN $2::float8 ELSE $1 END; $$ +LANGUAGE sql IMMUTABLE STRICT; + +CREATE FUNCTION nanvl(numeric, varchar) +RETURNS numeric AS +$$ SELECT CASE WHEN $1 = 'NaN' THEN $2::numeric ELSE $1 END; $$ +LANGUAGE sql IMMUTABLE STRICT; + +CREATE FUNCTION dump("any") +RETURNS varchar +AS 'MODULE_PATHNAME', 'orafce_dump' +LANGUAGE C; + +CREATE FUNCTION dump("any", integer) +RETURNS varchar +AS 'MODULE_PATHNAME', 'orafce_dump' +LANGUAGE C; + +CREATE SCHEMA plvstr; + +CREATE FUNCTION plvstr.rvrs(str text, start int, _end int) +RETURNS text +AS 'MODULE_PATHNAME','plvstr_rvrs' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION plvstr.rvrs(text, int, int) IS 'Reverse string or part of string'; + +CREATE FUNCTION plvstr.rvrs(str text, start int) +RETURNS text +AS $$ SELECT plvstr.rvrs($1,$2,NULL);$$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION plvstr.rvrs(text, int) IS 'Reverse string or part of string'; + +CREATE FUNCTION plvstr.rvrs(str text) +RETURNS text +AS $$ SELECT plvstr.rvrs($1,1,NULL);$$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION plvstr.rvrs(text) IS 'Reverse string or part of string'; + +CREATE FUNCTION pg_catalog.lnnvl(bool) +RETURNS bool +AS 'MODULE_PATHNAME','ora_lnnvl' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION pg_catalog.lnnvl(bool) IS ''; + +-- can't overwrite PostgreSQL functions!!!! + +CREATE SCHEMA oracle; + +CREATE FUNCTION oracle.substr(str text, start int) +RETURNS text +AS 'MODULE_PATHNAME','oracle_substr2' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION oracle.substr(text, int) IS 'Returns substring started on start_in to end'; + +CREATE FUNCTION oracle.substr(str text, start int, len int) +RETURNS text +AS 'MODULE_PATHNAME','oracle_substr3' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION oracle.substr(text, int, int) IS 'Returns substring started on start_in len chars'; + +CREATE OR REPLACE FUNCTION oracle.substr(numeric,numeric) +RETURNS text AS $$ +SELECT oracle.substr($1::text,trunc($2)::int); +$$ LANGUAGE SQL IMMUTABLE; + +CREATE OR REPLACE FUNCTION oracle.substr(numeric,numeric,numeric) +RETURNS text AS $$ +SELECT oracle.substr($1::text,trunc($2)::int,trunc($3)::int); +$$ LANGUAGE SQL IMMUTABLE; + +CREATE OR REPLACE FUNCTION oracle.substr(varchar,numeric) +RETURNS text AS $$ +SELECT oracle.substr($1,trunc($2)::int); +$$ LANGUAGE SQL IMMUTABLE; + +CREATE OR REPLACE FUNCTION oracle.substr(varchar,numeric,numeric) +RETURNS text AS $$ +SELECT oracle.substr($1,trunc($2)::int,trunc($3)::int); +$$ LANGUAGE SQL IMMUTABLE; + +--can't overwrite PostgreSQL DATE data type!!! + +CREATE DOMAIN oracle.date AS timestamp(0); + +CREATE OR REPLACE FUNCTION oracle.add_days_to_timestamp(oracle.date,integer) +RETURNS timestamp AS $$ +SELECT $1 + interval '1 day' * $2; +$$ LANGUAGE SQL IMMUTABLE; + +CREATE OR REPLACE FUNCTION oracle.subtract (oracle.date, integer) +RETURNS timestamp AS $$ +SELECT $1 - interval '1 day' * $2; +$$ LANGUAGE SQL IMMUTABLE; + +CREATE OR REPLACE FUNCTION oracle.add_days_to_timestamp(oracle.date,bigint) +RETURNS timestamp AS $$ +SELECT $1 + interval '1 day' * $2; +$$ LANGUAGE SQL IMMUTABLE; + +CREATE OR REPLACE FUNCTION oracle.subtract (oracle.date, bigint) +RETURNS timestamp AS $$ +SELECT $1 - interval '1 day' * $2; +$$ LANGUAGE SQL IMMUTABLE; + +CREATE OR REPLACE FUNCTION oracle.add_days_to_timestamp(oracle.date,smallint) +RETURNS timestamp AS $$ +SELECT $1 + interval '1 day' * $2; +$$ LANGUAGE SQL IMMUTABLE; + +CREATE OR REPLACE FUNCTION oracle.subtract (oracle.date, smallint) +RETURNS timestamp AS $$ +SELECT $1 - interval '1 day' * $2; +$$ LANGUAGE SQL IMMUTABLE; + +CREATE OR REPLACE FUNCTION oracle.add_days_to_timestamp(oracle.date,numeric) +RETURNS timestamp AS $$ +SELECT $1 + interval '1 day' * $2; +$$ LANGUAGE SQL IMMUTABLE; + +CREATE OR REPLACE FUNCTION oracle.subtract (oracle.date, numeric) +RETURNS timestamp AS $$ +SELECT $1 - interval '1 day' * $2; +$$ LANGUAGE SQL IMMUTABLE; + +CREATE OR REPLACE FUNCTION oracle.subtract(oracle.date,oracle.date) +RETURNS double precision AS $$ +SELECT date_part('epoch', ($1::timestamp - $2::timestamp)/3600/24); +$$ LANGUAGE SQL IMMUTABLE; + +CREATE OPERATOR oracle.+ ( + LEFTARG = oracle.date, + RIGHTARG = INTEGER, + PROCEDURE = oracle.add_days_to_timestamp +); + +CREATE OPERATOR oracle.- ( + LEFTARG = oracle.date, + RIGHTARG = INTEGER, + PROCEDURE = oracle.subtract +); + +CREATE OPERATOR oracle.+ ( + LEFTARG = oracle.date, + RIGHTARG = bigint, + PROCEDURE = oracle.add_days_to_timestamp +); + +CREATE OPERATOR oracle.- ( + LEFTARG = oracle.date, + RIGHTARG = bigint, + PROCEDURE = oracle.subtract +); + +CREATE OPERATOR oracle.+ ( + LEFTARG = oracle.date, + RIGHTARG = smallint, + PROCEDURE = oracle.add_days_to_timestamp +); + +CREATE OPERATOR oracle.- ( + LEFTARG = oracle.date, + RIGHTARG = smallint, + PROCEDURE = oracle.subtract +); + +CREATE OPERATOR oracle.+ ( + LEFTARG = oracle.date, + RIGHTARG = numeric, + PROCEDURE = oracle.add_days_to_timestamp +); + +CREATE OPERATOR oracle.- ( + LEFTARG = oracle.date, + RIGHTARG = numeric, + PROCEDURE = oracle.subtract +); + +CREATE OPERATOR oracle.- ( + LEFTARG = oracle.date, + RIGHTARG = oracle.date, + PROCEDURE = oracle.subtract +); + +CREATE FUNCTION oracle.add_months(TIMESTAMP WITH TIME ZONE,INTEGER) +RETURNS TIMESTAMP +AS $$ SELECT (pg_catalog.add_months($1::pg_catalog.date, $2) + $1::time)::oracle.date; $$ +LANGUAGE SQL IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION oracle.last_day(TIMESTAMPTZ) +RETURNS TIMESTAMP +AS $$ SELECT (date_trunc('MONTH', $1) + INTERVAL '1 MONTH - 1 day' + $1::time)::oracle.date; $$ +LANGUAGE SQL IMMUTABLE STRICT; + +CREATE FUNCTION oracle.months_between(TIMESTAMP WITH TIME ZONE,TIMESTAMP WITH TIME ZONE) +RETURNS NUMERIC +AS $$ SELECT pg_catalog.months_between($1::pg_catalog.date,$2::pg_catalog.date); $$ +LANGUAGE SQL IMMUTABLE STRICT; + +CREATE FUNCTION oracle.next_day(TIMESTAMP WITH TIME ZONE,INTEGER) +RETURNS TIMESTAMP +AS $$ SELECT (pg_catalog.next_day($1::pg_catalog.date,$2) + $1::time)::oracle.date; $$ +LANGUAGE SQL IMMUTABLE STRICT; + +CREATE FUNCTION oracle.next_day(TIMESTAMP WITH TIME ZONE,TEXT) +RETURNS TIMESTAMP +AS $$ SELECT (pg_catalog.next_day($1::pg_catalog.date,$2) + $1::time)::oracle.date; $$ +LANGUAGE SQL IMMUTABLE STRICT; + +CREATE FUNCTION oracle.to_date(TEXT) +RETURNS oracle.date +AS $$ SELECT pg_catalog.to_date($1)::oracle.date; $$ +LANGUAGE SQL STABLE STRICT; + +CREATE OR REPLACE FUNCTION oracle.to_date(TEXT,TEXT) +RETURNS oracle.date +AS $$ SELECT TO_TIMESTAMP($1,$2)::oracle.date; $$ +LANGUAGE SQL IMMUTABLE STRICT; + +CREATE FUNCTION oracle.to_char(timestamp) +RETURNS TEXT +AS 'MODULE_PATHNAME','orafce_to_char_timestamp' +LANGUAGE C STABLE STRICT; +COMMENT ON FUNCTION oracle.to_char(timestamp) IS 'Convert timestamp to string'; + +CREATE FUNCTION oracle.sysdate() +RETURNS oracle.date +AS 'MODULE_PATHNAME','orafce_sysdate' +LANGUAGE C STABLE STRICT; +COMMENT ON FUNCTION oracle.sysdate() IS 'Ruturns statement timestamp at server time zone'; + +CREATE FUNCTION oracle.sessiontimezone() +RETURNS text +AS 'MODULE_PATHNAME','orafce_sessiontimezone' +LANGUAGE C STABLE STRICT; +COMMENT ON FUNCTION oracle.sessiontimezone() IS 'Ruturns session time zone'; + +CREATE FUNCTION oracle.dbtimezone() +RETURNS text +AS 'MODULE_PATHNAME','orafce_dbtimezone' +LANGUAGE C STABLE STRICT; +COMMENT ON FUNCTION oracle.dbtimezone() IS 'Ruturns server time zone (orafce.timezone)'; + +-- emulation of dual table +CREATE VIEW public.dual AS SELECT 'X'::varchar AS dummy; +REVOKE ALL ON public.dual FROM PUBLIC; +GRANT SELECT, REFERENCES ON public.dual TO PUBLIC; + +-- this packege is emulation of dbms_output Oracle packege +-- + +CREATE SCHEMA dbms_output; + +CREATE FUNCTION dbms_output.enable(IN buffer_size int4) +RETURNS void +AS 'MODULE_PATHNAME','dbms_output_enable' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION dbms_output.enable(IN int4) IS 'Enable package functionality'; + +CREATE FUNCTION dbms_output.enable() +RETURNS void +AS 'MODULE_PATHNAME','dbms_output_enable_default' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION dbms_output.enable() IS 'Enable package functionality'; + +CREATE FUNCTION dbms_output.disable() +RETURNS void +AS 'MODULE_PATHNAME','dbms_output_disable' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION dbms_output.disable() IS 'Disable package functionality'; + +CREATE FUNCTION dbms_output.serveroutput(IN bool) +RETURNS void +AS 'MODULE_PATHNAME','dbms_output_serveroutput' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION dbms_output.serveroutput(IN bool) IS 'Set drowing output'; + +CREATE FUNCTION dbms_output.put(IN a text) +RETURNS void +AS 'MODULE_PATHNAME','dbms_output_put' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION dbms_output.put(IN text) IS 'Put some text to output'; + +CREATE FUNCTION dbms_output.put_line(IN a text) +RETURNS void +AS 'MODULE_PATHNAME','dbms_output_put_line' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION dbms_output.put_line(IN text) IS 'Put line to output'; + +CREATE FUNCTION dbms_output.new_line() +RETURNS void +AS 'MODULE_PATHNAME','dbms_output_new_line' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION dbms_output.new_line() IS 'Put new line char to output'; + +CREATE FUNCTION dbms_output.get_line(OUT line text, OUT status int4) +AS 'MODULE_PATHNAME','dbms_output_get_line' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION dbms_output.get_line(OUT text, OUT int4) IS 'Get line from output buffer'; + + +CREATE FUNCTION dbms_output.get_lines(OUT lines text[], INOUT numlines int4) +AS 'MODULE_PATHNAME','dbms_output_get_lines' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION dbms_output.get_lines(OUT text[], INOUT int4) IS 'Get lines from output buffer'; + + +-- others functions + +CREATE FUNCTION nvl(anyelement, anyelement) +RETURNS anyelement +AS 'MODULE_PATHNAME','ora_nvl' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION nvl2(anyelement, anyelement, anyelement) +RETURNS anyelement +AS 'MODULE_PATHNAME','ora_nvl2' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION nvl2(anyelement, anyelement, anyelement) IS ''; + +CREATE FUNCTION public.decode(anyelement, anyelement, text) +RETURNS text +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, text, text) +RETURNS text +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, text, anyelement, text) +RETURNS text +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, text, anyelement, text, text) +RETURNS text +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, text, anyelement, text, anyelement, text) +RETURNS text +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, text, anyelement, text, anyelement, text, text) +RETURNS text +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, bpchar) +RETURNS bpchar +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, bpchar, bpchar) +RETURNS bpchar +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, bpchar, anyelement, bpchar) +RETURNS bpchar +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, bpchar, anyelement, bpchar, bpchar) +RETURNS bpchar +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, bpchar, anyelement, bpchar, anyelement, bpchar) +RETURNS bpchar +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, bpchar, anyelement, bpchar, anyelement, bpchar, bpchar) +RETURNS bpchar +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, integer) +RETURNS integer +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, integer, integer) +RETURNS integer +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, integer, anyelement, integer) +RETURNS integer +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, integer, anyelement, integer, integer) +RETURNS integer +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, integer, anyelement, integer, anyelement, integer) +RETURNS integer +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, integer, anyelement, integer, anyelement, integer, integer) +RETURNS integer +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, bigint) +RETURNS bigint +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, bigint, bigint) +RETURNS bigint +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, bigint, anyelement, bigint) +RETURNS bigint +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, bigint, anyelement, bigint, bigint) +RETURNS bigint +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, bigint, anyelement, bigint, anyelement, bigint) +RETURNS bigint +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, bigint, anyelement, bigint, anyelement, bigint, bigint) +RETURNS bigint +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, numeric) +RETURNS numeric +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, numeric, numeric) +RETURNS numeric +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, numeric, anyelement, numeric) +RETURNS numeric +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, numeric, anyelement, numeric, numeric) +RETURNS numeric +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, numeric, anyelement, numeric, anyelement, numeric) +RETURNS numeric +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, numeric, anyelement, numeric, anyelement, numeric, numeric) +RETURNS numeric +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, date) +RETURNS date +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, date, date) +RETURNS date +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, date, anyelement, date) +RETURNS date +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, date, anyelement, date, date) +RETURNS date +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, date, anyelement, date, anyelement, date) +RETURNS date +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, date, anyelement, date, anyelement, date, date) +RETURNS date +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, time) +RETURNS time +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, time, time) +RETURNS time +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, time, anyelement, time) +RETURNS time +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, time, anyelement, time, time) +RETURNS time +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, time, anyelement, time, anyelement, time) +RETURNS time +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, time, anyelement, time, anyelement, time, time) +RETURNS time +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, timestamp) +RETURNS timestamp +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, timestamp, timestamp) +RETURNS timestamp +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, timestamp, anyelement, timestamp) +RETURNS timestamp +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, timestamp, anyelement, timestamp, timestamp) +RETURNS timestamp +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, timestamp, anyelement, timestamp, anyelement, timestamp) +RETURNS timestamp +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, timestamp, anyelement, timestamp, anyelement, timestamp, timestamp) +RETURNS timestamp +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, timestamptz) +RETURNS timestamptz +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, timestamptz, timestamptz) +RETURNS timestamptz +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, timestamptz, anyelement, timestamptz) +RETURNS timestamptz +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, timestamptz, anyelement, timestamptz, timestamptz) +RETURNS timestamptz +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, timestamptz, anyelement, timestamptz, anyelement, timestamptz) +RETURNS timestamptz +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION public.decode(anyelement, anyelement, timestamptz, anyelement, timestamptz, anyelement, timestamptz, timestamptz) +RETURNS timestamptz +AS 'MODULE_PATHNAME', 'ora_decode' +LANGUAGE C IMMUTABLE; + + +CREATE SCHEMA dbms_pipe; + +CREATE FUNCTION dbms_pipe.pack_message(text) +RETURNS void +AS 'MODULE_PATHNAME','dbms_pipe_pack_message_text' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION dbms_pipe.pack_message(text) IS 'Add text field to message'; + +CREATE FUNCTION dbms_pipe.unpack_message_text() +RETURNS text +AS 'MODULE_PATHNAME','dbms_pipe_unpack_message_text' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION dbms_pipe.unpack_message_text() IS 'Get text fiedl from message'; + +CREATE FUNCTION dbms_pipe.receive_message(text, int) +RETURNS int +AS 'MODULE_PATHNAME','dbms_pipe_receive_message' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION dbms_pipe.receive_message(text, int) IS 'Receive message from pipe'; + +CREATE FUNCTION dbms_pipe.receive_message(text) +RETURNS int +AS $$SELECT dbms_pipe.receive_message($1,NULL::int);$$ +LANGUAGE SQL VOLATILE; +COMMENT ON FUNCTION dbms_pipe.receive_message(text) IS 'Receive message from pipe'; + +CREATE FUNCTION dbms_pipe.send_message(text, int, int) +RETURNS int +AS 'MODULE_PATHNAME','dbms_pipe_send_message' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION dbms_pipe.send_message(text, int, int) IS 'Send message to pipe'; + +CREATE FUNCTION dbms_pipe.send_message(text, int) +RETURNS int +AS $$SELECT dbms_pipe.send_message($1,$2,NULL);$$ +LANGUAGE SQL VOLATILE; +COMMENT ON FUNCTION dbms_pipe.send_message(text, int) IS 'Send message to pipe'; + +CREATE FUNCTION dbms_pipe.send_message(text) +RETURNS int +AS $$SELECT dbms_pipe.send_message($1,NULL,NULL);$$ +LANGUAGE SQL VOLATILE; +COMMENT ON FUNCTION dbms_pipe.send_message(text) IS 'Send message to pipe'; + +CREATE FUNCTION dbms_pipe.unique_session_name() +RETURNS varchar +AS 'MODULE_PATHNAME','dbms_pipe_unique_session_name' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION dbms_pipe.unique_session_name() IS 'Returns unique session name'; + +CREATE FUNCTION dbms_pipe.__list_pipes() +RETURNS SETOF RECORD +AS 'MODULE_PATHNAME','dbms_pipe_list_pipes' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION dbms_pipe.__list_pipes() IS ''; + +CREATE VIEW dbms_pipe.db_pipes +AS SELECT * FROM dbms_pipe.__list_pipes() AS (Name varchar, Items int, Size int, "limit" int, "private" bool, "owner" varchar); + +CREATE FUNCTION dbms_pipe.next_item_type() +RETURNS int +AS 'MODULE_PATHNAME','dbms_pipe_next_item_type' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION dbms_pipe.next_item_type() IS 'Returns type of next field in message'; + +CREATE FUNCTION dbms_pipe.create_pipe(text, int, bool) +RETURNS void +AS 'MODULE_PATHNAME','dbms_pipe_create_pipe' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION dbms_pipe.create_pipe(text, int, bool) IS 'Create named pipe'; + +CREATE FUNCTION dbms_pipe.create_pipe(text, int) +RETURNS void +AS 'MODULE_PATHNAME','dbms_pipe_create_pipe_2' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION dbms_pipe.create_pipe(text, int) IS 'Create named pipe'; + +CREATE FUNCTION dbms_pipe.create_pipe(text) +RETURNS void +AS 'MODULE_PATHNAME','dbms_pipe_create_pipe_1' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION dbms_pipe.create_pipe(text) IS 'Create named pipe'; + +CREATE FUNCTION dbms_pipe.reset_buffer() +RETURNS void +AS 'MODULE_PATHNAME','dbms_pipe_reset_buffer' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION dbms_pipe.reset_buffer() IS 'Clean input buffer'; + +CREATE FUNCTION dbms_pipe.purge(text) +RETURNS void +AS 'MODULE_PATHNAME','dbms_pipe_purge' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION dbms_pipe.purge(text) IS 'Clean pipe'; + +CREATE FUNCTION dbms_pipe.remove_pipe(text) +RETURNS void +AS 'MODULE_PATHNAME','dbms_pipe_remove_pipe' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION dbms_pipe.remove_pipe(text) IS 'Destroy pipe'; + +CREATE FUNCTION dbms_pipe.pack_message(date) +RETURNS void +AS 'MODULE_PATHNAME','dbms_pipe_pack_message_date' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION dbms_pipe.pack_message(date) IS 'Add date field to message'; + +CREATE FUNCTION dbms_pipe.unpack_message_date() +RETURNS date +AS 'MODULE_PATHNAME','dbms_pipe_unpack_message_date' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION dbms_pipe.unpack_message_date() IS 'Get date field from message'; + +CREATE FUNCTION dbms_pipe.pack_message(timestamp with time zone) +RETURNS void +AS 'MODULE_PATHNAME','dbms_pipe_pack_message_timestamp' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION dbms_pipe.pack_message(timestamp with time zone) IS 'Add timestamp field to message'; + +CREATE FUNCTION dbms_pipe.unpack_message_timestamp() +RETURNS timestamp with time zone +AS 'MODULE_PATHNAME','dbms_pipe_unpack_message_timestamp' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION dbms_pipe.unpack_message_timestamp() IS 'Get timestamp field from message'; + +CREATE FUNCTION dbms_pipe.pack_message(numeric) +RETURNS void +AS 'MODULE_PATHNAME','dbms_pipe_pack_message_number' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION dbms_pipe.pack_message(numeric) IS 'Add numeric field to message'; + +CREATE FUNCTION dbms_pipe.unpack_message_number() +RETURNS numeric +AS 'MODULE_PATHNAME','dbms_pipe_unpack_message_number' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION dbms_pipe.unpack_message_number() IS 'Get numeric field from message'; + +CREATE FUNCTION dbms_pipe.pack_message(integer) +RETURNS void +AS 'MODULE_PATHNAME','dbms_pipe_pack_message_integer' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION dbms_pipe.pack_message(integer) IS 'Add numeric field to message'; + +CREATE FUNCTION dbms_pipe.pack_message(bigint) +RETURNS void +AS 'MODULE_PATHNAME','dbms_pipe_pack_message_bigint' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION dbms_pipe.pack_message(bigint) IS 'Add numeric field to message'; + +CREATE FUNCTION dbms_pipe.pack_message(bytea) +RETURNS void +AS 'MODULE_PATHNAME','dbms_pipe_pack_message_bytea' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION dbms_pipe.pack_message(bytea) IS 'Add bytea field to message'; + +CREATE FUNCTION dbms_pipe.unpack_message_bytea() +RETURNS bytea +AS 'MODULE_PATHNAME','dbms_pipe_unpack_message_bytea' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION dbms_pipe.unpack_message_bytea() IS 'Get bytea field from message'; + +CREATE FUNCTION dbms_pipe.pack_message(record) +RETURNS void +AS 'MODULE_PATHNAME','dbms_pipe_pack_message_record' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION dbms_pipe.pack_message(record) IS 'Add record field to message'; + +CREATE FUNCTION dbms_pipe.unpack_message_record() +RETURNS record +AS 'MODULE_PATHNAME','dbms_pipe_unpack_message_record' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION dbms_pipe.unpack_message_record() IS 'Get record field from message'; + + + +-- follow package PLVdate emulation + +CREATE SCHEMA plvdate; + +CREATE FUNCTION plvdate.add_bizdays(date, int) +RETURNS date +AS 'MODULE_PATHNAME','plvdate_add_bizdays' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION plvdate.add_bizdays(date, int) IS 'Get the date created by adding business days to a date'; + +CREATE FUNCTION plvdate.nearest_bizday(date) +RETURNS date +AS 'MODULE_PATHNAME','plvdate_nearest_bizday' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION plvdate.nearest_bizday(date) IS 'Get the nearest business date to a given date, user defined'; + +CREATE FUNCTION plvdate.next_bizday(date) +RETURNS date +AS 'MODULE_PATHNAME','plvdate_next_bizday' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION plvdate.next_bizday(date) IS 'Get the next business date from a given date, user defined'; + +CREATE FUNCTION plvdate.bizdays_between(date, date) +RETURNS int +AS 'MODULE_PATHNAME','plvdate_bizdays_between' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION plvdate.bizdays_between(date, date) IS 'Get the number of business days between two dates'; + +CREATE FUNCTION plvdate.prev_bizday(date) +RETURNS date +AS 'MODULE_PATHNAME','plvdate_prev_bizday' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION plvdate.prev_bizday(date) IS 'Get the previous business date from a given date'; + +CREATE FUNCTION plvdate.isbizday(date) +RETURNS bool +AS 'MODULE_PATHNAME','plvdate_isbizday' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION plvdate.isbizday(date) IS 'Call this function to determine if a date is a business day'; + +CREATE FUNCTION plvdate.set_nonbizday(text) +RETURNS void +AS 'MODULE_PATHNAME','plvdate_set_nonbizday_dow' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION plvdate.set_nonbizday(text) IS 'Set day of week as non bussines day'; + +CREATE FUNCTION plvdate.unset_nonbizday(text) +RETURNS void +AS 'MODULE_PATHNAME','plvdate_unset_nonbizday_dow' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION plvdate.unset_nonbizday(text) IS 'Unset day of week as non bussines day'; + +CREATE FUNCTION plvdate.set_nonbizday(date, bool) +RETURNS void +AS 'MODULE_PATHNAME','plvdate_set_nonbizday_day' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION plvdate.set_nonbizday(date, bool) IS 'Set day as non bussines day, if repeat is true, then day is nonbiz every year'; + +CREATE FUNCTION plvdate.unset_nonbizday(date, bool) +RETURNS void +AS 'MODULE_PATHNAME','plvdate_unset_nonbizday_day' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION plvdate.unset_nonbizday(date, bool) IS 'Unset day as non bussines day, if repeat is true, then day is nonbiz every year'; + +CREATE FUNCTION plvdate.set_nonbizday(date) +RETURNS bool +AS $$SELECT plvdate.set_nonbizday($1, false); SELECT NULL::boolean;$$ +LANGUAGE SQL VOLATILE STRICT; +COMMENT ON FUNCTION plvdate.set_nonbizday(date) IS 'Set day as non bussines day'; + +CREATE FUNCTION plvdate.unset_nonbizday(date) +RETURNS bool +AS $$SELECT plvdate.unset_nonbizday($1, false); SELECT NULL::boolean;$$ +LANGUAGE SQL VOLATILE STRICT; +COMMENT ON FUNCTION plvdate.unset_nonbizday(date) IS 'Unset day as non bussines day'; + +CREATE FUNCTION plvdate.use_easter(bool) +RETURNS void +AS 'MODULE_PATHNAME','plvdate_use_easter' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION plvdate.use_easter(bool) IS 'Easter Sunday and easter monday will be holiday'; + +CREATE FUNCTION plvdate.use_easter() +RETURNS bool +AS $$SELECT plvdate.use_easter(true); SELECT NULL::boolean;$$ +LANGUAGE SQL VOLATILE STRICT; +COMMENT ON FUNCTION plvdate.use_easter() IS 'Easter Sunday and easter monday will be holiday'; + +CREATE FUNCTION plvdate.unuse_easter() +RETURNS bool +AS $$SELECT plvdate.use_easter(false); SELECT NULL::boolean;$$ +LANGUAGE SQL VOLATILE STRICT; +COMMENT ON FUNCTION plvdate.unuse_easter() IS 'Easter Sunday and easter monday will not be holiday'; + +CREATE FUNCTION plvdate.using_easter() +RETURNS bool +AS 'MODULE_PATHNAME','plvdate_using_easter' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION plvdate.using_easter() IS 'Use easter?'; + +CREATE FUNCTION plvdate.use_great_friday(bool) +RETURNS void +AS 'MODULE_PATHNAME','plvdate_use_great_friday' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION plvdate.use_great_friday(bool) IS 'Great Friday will be holiday'; + +CREATE FUNCTION plvdate.use_great_friday() +RETURNS bool +AS $$SELECT plvdate.use_great_friday(true); SELECT NULL::boolean;$$ +LANGUAGE SQL VOLATILE STRICT; +COMMENT ON FUNCTION plvdate.use_great_friday() IS 'Great Friday will be holiday'; + +CREATE FUNCTION plvdate.unuse_great_friday() +RETURNS bool +AS $$SELECT plvdate.use_great_friday(false); SELECT NULL::boolean;$$ +LANGUAGE SQL VOLATILE STRICT; +COMMENT ON FUNCTION plvdate.unuse_great_friday() IS 'Great Friday will not be holiday'; + +CREATE FUNCTION plvdate.using_great_friday() +RETURNS bool +AS 'MODULE_PATHNAME','plvdate_using_great_friday' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION plvdate.using_great_friday() IS 'Use Great Friday?'; + +CREATE FUNCTION plvdate.include_start(bool) +RETURNS void +AS 'MODULE_PATHNAME','plvdate_include_start' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION plvdate.include_start(bool) IS 'Include starting date in bizdays_between calculation'; + +CREATE FUNCTION plvdate.include_start() +RETURNS bool +AS $$SELECT plvdate.include_start(true); SELECT NULL::boolean;$$ +LANGUAGE SQL VOLATILE STRICT; +COMMENT ON FUNCTION plvdate.include_start() IS ''; + +CREATE FUNCTION plvdate.noinclude_start() +RETURNS bool +AS $$SELECT plvdate.include_start(false); SELECT NULL::boolean;$$ +LANGUAGE SQL VOLATILE STRICT; +COMMENT ON FUNCTION plvdate.noinclude_start() IS ''; + +CREATE FUNCTION plvdate.including_start() +RETURNS bool +AS 'MODULE_PATHNAME','plvdate_including_start' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION plvdate.including_start() IS ''; + +CREATE FUNCTION plvdate.version() +RETURNS cstring +AS 'MODULE_PATHNAME','plvdate_version' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION plvdate.version() IS ''; + +CREATE FUNCTION plvdate.default_holidays(text) +RETURNS void +AS 'MODULE_PATHNAME','plvdate_default_holidays' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION plvdate.default_holidays(text) IS 'Load calendar for some nations'; + +CREATE FUNCTION plvdate.days_inmonth(date) +RETURNS integer +AS 'MODULE_PATHNAME','plvdate_days_inmonth' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION plvdate.days_inmonth(date) IS 'Returns number of days in month'; + +CREATE FUNCTION plvdate.isleapyear(date) +RETURNS bool +AS 'MODULE_PATHNAME','plvdate_isleapyear' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION plvdate.isleapyear(date) IS 'Is leap year'; + + +-- PLVstr package + + +CREATE FUNCTION plvstr.normalize(str text) +RETURNS varchar +AS 'MODULE_PATHNAME','plvstr_normalize' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION plvstr.normalize(text) IS 'Replace white chars by space, replace spaces by space'; + +CREATE FUNCTION plvstr.is_prefix(str text, prefix text, cs bool) +RETURNS bool +AS 'MODULE_PATHNAME','plvstr_is_prefix_text' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION plvstr.is_prefix(text, text, bool) IS 'Returns true, if prefix is prefix of str'; + +CREATE FUNCTION plvstr.is_prefix(str text, prefix text) +RETURNS bool +AS $$ SELECT plvstr.is_prefix($1,$2,true);$$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION plvstr.is_prefix(text, text) IS 'Returns true, if prefix is prefix of str'; + +CREATE FUNCTION plvstr.is_prefix(str int, prefix int) +RETURNS bool +AS 'MODULE_PATHNAME','plvstr_is_prefix_int' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION plvstr.is_prefix(int, int) IS 'Returns true, if prefix is prefix of str'; + +CREATE FUNCTION plvstr.is_prefix(str bigint, prefix bigint) +RETURNS bool +AS 'MODULE_PATHNAME','plvstr_is_prefix_int64' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION plvstr.is_prefix(bigint, bigint) IS 'Returns true, if prefix is prefix of str'; + +CREATE FUNCTION plvstr.substr(str text, start int, len int) +RETURNS varchar +AS 'MODULE_PATHNAME','plvstr_substr3' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION plvstr.substr(text, int, int) IS 'Returns substring started on start_in to end'; + +CREATE FUNCTION plvstr.substr(str text, start int) +RETURNS varchar +AS 'MODULE_PATHNAME','plvstr_substr2' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION plvstr.substr(text, int) IS 'Returns substring started on start_in to end'; + +CREATE FUNCTION plvstr.instr(str text, patt text, start int, nth int) +RETURNS int +AS 'MODULE_PATHNAME','plvstr_instr4' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION plvstr.instr(text, text, int, int) IS 'Search pattern in string'; + +CREATE FUNCTION plvstr.instr(str text, patt text, start int) +RETURNS int +AS 'MODULE_PATHNAME','plvstr_instr3' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION plvstr.instr(text, text, int) IS 'Search pattern in string'; + +CREATE FUNCTION plvstr.instr(str text, patt text) +RETURNS int +AS 'MODULE_PATHNAME','plvstr_instr2' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION plvstr.instr(text, text) IS 'Search pattern in string'; + +CREATE FUNCTION plvstr.lpart(str text, div text, start int, nth int, all_if_notfound bool) +RETURNS text +AS 'MODULE_PATHNAME','plvstr_lpart' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION plvstr.lpart(text, text, int, int, bool) IS 'Call this function to return the left part of a string'; + +CREATE FUNCTION plvstr.lpart(str text, div text, start int, nth int) +RETURNS text +AS $$ SELECT plvstr.lpart($1,$2, $3, $4, false); $$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION plvstr.lpart(text, text, int, int) IS 'Call this function to return the left part of a string'; + +CREATE FUNCTION plvstr.lpart(str text, div text, start int) +RETURNS text +AS $$ SELECT plvstr.lpart($1,$2, $3, 1, false); $$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION plvstr.lpart(text, text, int) IS 'Call this function to return the left part of a string'; + +CREATE FUNCTION plvstr.lpart(str text, div text) +RETURNS text +AS $$ SELECT plvstr.lpart($1,$2, 1, 1, false); $$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION plvstr.lpart(text, text) IS 'Call this function to return the left part of a string'; + +CREATE FUNCTION plvstr.rpart(str text, div text, start int, nth int, all_if_notfound bool) +RETURNS text +AS 'MODULE_PATHNAME','plvstr_rpart' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION plvstr.rpart(text, text, int, int, bool) IS 'Call this function to return the right part of a string'; + +CREATE FUNCTION plvstr.rpart(str text, div text, start int, nth int) +RETURNS text +AS $$ SELECT plvstr.rpart($1,$2, $3, $4, false); $$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION plvstr.rpart(text, text, int, int) IS 'Call this function to return the right part of a string'; + +CREATE FUNCTION plvstr.rpart(str text, div text, start int) +RETURNS text +AS $$ SELECT plvstr.rpart($1,$2, $3, 1, false); $$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION plvstr.rpart(text, text, int) IS 'Call this function to return the right part of a string'; + +CREATE FUNCTION plvstr.rpart(str text, div text) +RETURNS text +AS $$ SELECT plvstr.rpart($1,$2, 1, 1, false); $$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION plvstr.rpart(text, text) IS 'Call this function to return the right part of a string'; + +CREATE FUNCTION plvstr.lstrip(str text, substr text, num int) +RETURNS text +AS 'MODULE_PATHNAME','plvstr_lstrip' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION plvstr.lstrip(text, text, int) IS 'Call this function to remove characters from the beginning '; + +CREATE FUNCTION plvstr.lstrip(str text, substr text) +RETURNS text +AS $$ SELECT plvstr.lstrip($1, $2, 1); $$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION plvstr.lstrip(text, text) IS 'Call this function to remove characters from the beginning '; + +CREATE FUNCTION plvstr.rstrip(str text, substr text, num int) +RETURNS text +AS 'MODULE_PATHNAME','plvstr_rstrip' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION plvstr.rstrip(text, text, int) IS 'Call this function to remove characters from the end'; + +CREATE FUNCTION plvstr.rstrip(str text, substr text) +RETURNS text +AS $$ SELECT plvstr.rstrip($1, $2, 1); $$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION plvstr.rstrip(text, text) IS 'Call this function to remove characters from the end'; + + + +CREATE FUNCTION plvstr.swap(str text, replace text, start int, length int) +RETURNS text +AS 'MODULE_PATHNAME','plvstr_swap' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION plvstr.swap(text,text, int, int) IS 'Replace a substring in a string with a specified string'; + +CREATE FUNCTION plvstr.swap(str text, replace text) +RETURNS text +AS $$ SELECT plvstr.swap($1,$2,1, NULL);$$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION plvstr.swap(text,text) IS 'Replace a substring in a string with a specified string'; + +CREATE FUNCTION plvstr.betwn(str text, start int, _end int, inclusive bool) +RETURNS text +AS 'MODULE_PATHNAME','plvstr_betwn_i' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION plvstr.betwn(text, int, int, bool) IS 'Find the Substring Between Start and End Locations'; + +CREATE FUNCTION plvstr.betwn(str text, start int, _end int) +RETURNS text +AS $$ SELECT plvstr.betwn($1,$2,$3,true);$$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION plvstr.betwn(text, int, int) IS 'Find the Substring Between Start and End Locations'; + +CREATE FUNCTION plvstr.betwn(str text, start text, _end text, startnth int, endnth int, inclusive bool, gotoend bool) +RETURNS text +AS 'MODULE_PATHNAME','plvstr_betwn_c' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION plvstr.betwn(text, text, text, int, int, bool, bool) IS 'Find the Substring Between Start and End Locations'; + +CREATE FUNCTION plvstr.betwn(str text, start text, _end text) +RETURNS text +AS $$ SELECT plvstr.betwn($1,$2,$3,1,1,true,false);$$ +LANGUAGE SQL IMMUTABLE; +COMMENT ON FUNCTION plvstr.betwn(text, text, text) IS 'Find the Substring Between Start and End Locations'; + +CREATE FUNCTION plvstr.betwn(str text, start text, _end text, startnth int, endnth int) +RETURNS text +AS $$ SELECT plvstr.betwn($1,$2,$3,$4,$5,true,false);$$ +LANGUAGE SQL IMMUTABLE; +COMMENT ON FUNCTION plvstr.betwn(text, text, text, int, int) IS 'Find the Substring Between Start and End Locations'; + +CREATE SCHEMA plvchr; + +CREATE FUNCTION plvchr.nth(str text, n int) +RETURNS text +AS 'MODULE_PATHNAME','plvchr_nth' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION plvchr.nth(text, int) IS 'Call this function to return the Nth character in a string'; + +CREATE FUNCTION plvchr.first(str text) +RETURNS varchar +AS 'MODULE_PATHNAME','plvchr_first' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION plvchr.first(text) IS 'Call this function to return the first character in a string'; + +CREATE FUNCTION plvchr.last(str text) +RETURNS varchar +AS 'MODULE_PATHNAME','plvchr_last' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION plvchr.last(text) IS 'Call this function to return the last character in a string'; + +CREATE FUNCTION plvchr._is_kind(str text, kind int) +RETURNS bool +AS 'MODULE_PATHNAME','plvchr_is_kind_a' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION plvchr._is_kind(text, int) IS ''; + +CREATE FUNCTION plvchr._is_kind(c int, kind int) +RETURNS bool +AS 'MODULE_PATHNAME','plvchr_is_kind_i' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION plvchr._is_kind(int, int) IS ''; + +CREATE FUNCTION plvchr.is_blank(c int) +RETURNS BOOL +AS $$ SELECT plvchr._is_kind($1, 1);$$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION plvchr.is_blank(int) IS ''; + +CREATE FUNCTION plvchr.is_blank(c text) +RETURNS BOOL +AS $$ SELECT plvchr._is_kind($1, 1);$$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION plvchr.is_blank(text) IS ''; + +CREATE FUNCTION plvchr.is_digit(c int) +RETURNS BOOL +AS $$ SELECT plvchr._is_kind($1, 2);$$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION plvchr.is_digit(int) IS ''; + +CREATE FUNCTION plvchr.is_digit(c text) +RETURNS BOOL +AS $$ SELECT plvchr._is_kind($1, 2);$$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION plvchr.is_digit(text) IS ''; + +CREATE FUNCTION plvchr.is_quote(c int) +RETURNS BOOL +AS $$ SELECT plvchr._is_kind($1, 3);$$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION plvchr.is_quote(int) IS ''; + +CREATE FUNCTION plvchr.is_quote(c text) +RETURNS BOOL +AS $$ SELECT plvchr._is_kind($1, 3);$$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION plvchr.is_quote(text) IS ''; + +CREATE FUNCTION plvchr.is_other(c int) +RETURNS BOOL +AS $$ SELECT plvchr._is_kind($1, 4);$$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION plvchr.is_other(int) IS ''; + +CREATE FUNCTION plvchr.is_other(c text) +RETURNS BOOL +AS $$ SELECT plvchr._is_kind($1, 4);$$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION plvchr.is_other(text) IS ''; + +CREATE FUNCTION plvchr.is_letter(c int) +RETURNS BOOL +AS $$ SELECT plvchr._is_kind($1, 5);$$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION plvchr.is_letter(int) IS ''; + +CREATE FUNCTION plvchr.is_letter(c text) +RETURNS BOOL +AS $$ SELECT plvchr._is_kind($1, 5);$$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION plvchr.is_letter(text) IS ''; + +CREATE FUNCTION plvchr.char_name(c text) +RETURNS varchar +AS 'MODULE_PATHNAME','plvchr_char_name' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION plvchr.char_name(text) IS ''; + +CREATE FUNCTION plvstr.left(str text, n int) +RETURNS varchar +AS 'MODULE_PATHNAME', 'plvstr_left' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION plvstr.left(text, int) IS 'Returns firs num_in charaters. You can use negative num_in'; + +CREATE FUNCTION plvstr.right(str text, n int) +RETURNS varchar +AS 'MODULE_PATHNAME','plvstr_right' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION plvstr.right(text, int) IS 'Returns last num_in charaters. You can use negative num_ni'; + +CREATE FUNCTION plvchr.quoted1(str text) +RETURNS varchar +AS $$SELECT ''''||$1||'''';$$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION plvchr.quoted1(text) IS E'Quoted text between '''; + +CREATE FUNCTION plvchr.quoted2(str text) +RETURNS varchar +AS $$SELECT '"'||$1||'"';$$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION plvchr.quoted2(text) IS 'Quoted text between "'; + +CREATE FUNCTION plvchr.stripped(str text, char_in text) +RETURNS varchar +AS $$ SELECT TRANSLATE($1, 'A'||$2, 'A'); $$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION plvchr.stripped(text, text) IS 'Strips a string of all instances of the specified characters'; + +-- dbms_alert + +CREATE SCHEMA dbms_alert; + +CREATE FUNCTION dbms_alert.register(name text) +RETURNS void +AS 'MODULE_PATHNAME','dbms_alert_register' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION dbms_alert.register(text) IS 'Register session as recipient of alert name'; + +CREATE FUNCTION dbms_alert.remove(name text) +RETURNS void +AS 'MODULE_PATHNAME','dbms_alert_remove' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION dbms_alert.remove(text) IS 'Remove session as recipient of alert name'; + +CREATE FUNCTION dbms_alert.removeall() +RETURNS void +AS 'MODULE_PATHNAME','dbms_alert_removeall' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION dbms_alert.removeall() IS 'Remove registration for all alerts'; + +CREATE FUNCTION dbms_alert._signal(name text, message text) +RETURNS void +AS 'MODULE_PATHNAME','dbms_alert_signal' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION dbms_alert._signal(text, text) IS ''; + +CREATE FUNCTION dbms_alert.waitany(OUT name text, OUT message text, OUT status integer, timeout float8) +RETURNS record +AS 'MODULE_PATHNAME','dbms_alert_waitany' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION dbms_alert.waitany(OUT text, OUT text, OUT integer, float8) IS 'Wait for any signal'; + +CREATE FUNCTION dbms_alert.waitone(name text, OUT message text, OUT status integer, timeout float8) +RETURNS record +AS 'MODULE_PATHNAME','dbms_alert_waitone' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION dbms_alert.waitone(text, OUT text, OUT integer, float8) IS 'Wait for specific signal'; + +CREATE FUNCTION dbms_alert.set_defaults(sensitivity float8) +RETURNS void +AS 'MODULE_PATHNAME','dbms_alert_set_defaults' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION dbms_alert.set_defaults(float8) IS ''; + +CREATE FUNCTION dbms_alert.defered_signal() +RETURNS trigger +AS 'MODULE_PATHNAME','dbms_alert_defered_signal' +LANGUAGE C SECURITY DEFINER; +REVOKE ALL ON FUNCTION dbms_alert.defered_signal() FROM PUBLIC; + +CREATE FUNCTION dbms_alert.signal(_event text, _message text) +RETURNS void +AS 'MODULE_PATHNAME','dbms_alert_signal' +LANGUAGE C SECURITY DEFINER; +COMMENT ON FUNCTION dbms_alert.signal(text, text) IS 'Emit signal to all recipients'; + +CREATE SCHEMA plvsubst; + +CREATE FUNCTION plvsubst.string(template_in text, values_in text[], subst text) +RETURNS text +AS 'MODULE_PATHNAME','plvsubst_string_array' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION plvsubst.string(text, text[], text) IS 'Scans a string for all instances of the substitution keyword and replace it with the next value in the substitution values list'; + +CREATE FUNCTION plvsubst.string(template_in text, values_in text[]) +RETURNS text +AS $$SELECT plvsubst.string($1,$2, NULL);$$ +LANGUAGE SQL STRICT VOLATILE; +COMMENT ON FUNCTION plvsubst.string(text, text[]) IS 'Scans a string for all instances of the substitution keyword and replace it with the next value in the substitution values list'; + +CREATE FUNCTION plvsubst.string(template_in text, vals_in text, delim_in text, subst_in text) +RETURNS text +AS 'MODULE_PATHNAME','plvsubst_string_string' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION plvsubst.string(text, text, text, text) IS 'Scans a string for all instances of the substitution keyword and replace it with the next value in the substitution values list'; + +CREATE FUNCTION plvsubst.string(template_in text, vals_in text) +RETURNS text +AS 'MODULE_PATHNAME','plvsubst_string_string' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION plvsubst.string(text, text) IS 'Scans a string for all instances of the substitution keyword and replace it with the next value in the substitution values list'; + +CREATE FUNCTION plvsubst.string(template_in text, vals_in text, delim_in text) +RETURNS text +AS 'MODULE_PATHNAME','plvsubst_string_string' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION plvsubst.string(text, text, text) IS 'Scans a string for all instances of the substitution keyword and replace it with the next value in the substitution values list'; + +CREATE FUNCTION plvsubst.setsubst(str text) +RETURNS void +AS 'MODULE_PATHNAME','plvsubst_setsubst' +LANGUAGE C STRICT VOLATILE; +COMMENT ON FUNCTION plvsubst.setsubst(text) IS 'Change the substitution keyword'; + +CREATE FUNCTION plvsubst.setsubst() +RETURNS void +AS 'MODULE_PATHNAME','plvsubst_setsubst_default' +LANGUAGE C STRICT VOLATILE; +COMMENT ON FUNCTION plvsubst.setsubst() IS 'Change the substitution keyword to default %s'; + +CREATE FUNCTION plvsubst.subst() +RETURNS text +AS 'MODULE_PATHNAME','plvsubst_subst' +LANGUAGE C STRICT VOLATILE; +COMMENT ON FUNCTION plvsubst.subst() IS 'Retrieve the current substitution keyword'; + +CREATE SCHEMA dbms_utility; + +CREATE FUNCTION dbms_utility.format_call_stack(text) +RETURNS text +AS 'MODULE_PATHNAME','dbms_utility_format_call_stack1' +LANGUAGE C STRICT VOLATILE; +COMMENT ON FUNCTION dbms_utility.format_call_stack(text) IS 'Return formated call stack'; + +CREATE FUNCTION dbms_utility.format_call_stack() +RETURNS text +AS 'MODULE_PATHNAME','dbms_utility_format_call_stack0' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION dbms_utility.format_call_stack() IS 'Return formated call stack'; + +CREATE SCHEMA plvlex; + +CREATE FUNCTION plvlex.tokens(IN str text, IN skip_spaces bool, IN qualified_names bool, +OUT pos int, OUT token text, OUT code int, OUT class text, OUT separator text, OUT mod text) +RETURNS SETOF RECORD +AS 'MODULE_PATHNAME','plvlex_tokens' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION plvlex.tokens(text,bool,bool) IS 'Parse SQL string'; + +CREATE SCHEMA utl_file; +CREATE DOMAIN utl_file.file_type integer; + +CREATE FUNCTION utl_file.fopen(location text, filename text, open_mode text, max_linesize integer, encoding name) +RETURNS utl_file.file_type +AS 'MODULE_PATHNAME','utl_file_fopen' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION utl_file.fopen(text,text,text,integer,name) IS 'The FOPEN function open file and return file handle'; + +CREATE FUNCTION utl_file.fopen(location text, filename text, open_mode text, max_linesize integer) +RETURNS utl_file.file_type +AS 'MODULE_PATHNAME','utl_file_fopen' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION utl_file.fopen(text,text,text,integer) IS 'The FOPEN function open file and return file handle'; + +CREATE FUNCTION utl_file.fopen(location text, filename text, open_mode text) +RETURNS utl_file.file_type +AS $$SELECT utl_file.fopen($1, $2, $3, 1024); $$ +LANGUAGE SQL VOLATILE; +COMMENT ON FUNCTION utl_file.fopen(text,text,text,integer) IS 'The FOPEN function open file and return file handle'; + +CREATE FUNCTION utl_file.is_open(file utl_file.file_type) +RETURNS bool +AS 'MODULE_PATHNAME','utl_file_is_open' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION utl_file.is_open(utl_file.file_type) IS 'Functions returns true if handle points to file that is open'; + +CREATE FUNCTION utl_file.get_line(file utl_file.file_type, OUT buffer text) +AS 'MODULE_PATHNAME','utl_file_get_line' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION utl_file.get_line(utl_file.file_type) IS 'Returns one line from file'; + +CREATE FUNCTION utl_file.get_line(file utl_file.file_type, OUT buffer text, len integer) +AS 'MODULE_PATHNAME','utl_file_get_line' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION utl_file.get_line(utl_file.file_type, len integer) IS 'Returns one line from file'; + +CREATE FUNCTION utl_file.get_nextline(file utl_file.file_type, OUT buffer text) +AS 'MODULE_PATHNAME','utl_file_get_nextline' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION utl_file.get_nextline(utl_file.file_type) IS 'Returns one line from file or returns NULL'; + +CREATE FUNCTION utl_file.put(file utl_file.file_type, buffer text) +RETURNS bool +AS 'MODULE_PATHNAME','utl_file_put' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION utl_file.put(utl_file.file_type, text) IS 'Puts data to specified file'; + +CREATE FUNCTION utl_file.put(file utl_file.file_type, buffer anyelement) +RETURNS bool +AS $$SELECT utl_file.put($1, $2::text); $$ +LANGUAGE SQL VOLATILE; +COMMENT ON FUNCTION utl_file.put(utl_file.file_type, anyelement) IS 'Puts data to specified file'; + +CREATE FUNCTION utl_file.new_line(file utl_file.file_type) +RETURNS bool +AS 'MODULE_PATHNAME','utl_file_new_line' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION utl_file.new_line(file utl_file.file_type) IS 'Function inserts one ore more newline characters in specified file'; + +CREATE FUNCTION utl_file.new_line(file utl_file.file_type, lines int) +RETURNS bool +AS 'MODULE_PATHNAME','utl_file_new_line' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION utl_file.new_line(file utl_file.file_type) IS 'Function inserts one ore more newline characters in specified file'; + +CREATE FUNCTION utl_file.put_line(file utl_file.file_type, buffer text) +RETURNS bool +AS 'MODULE_PATHNAME','utl_file_put_line' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION utl_file.put_line(utl_file.file_type, text) IS 'Puts data to specified file and append newline character'; + +CREATE FUNCTION utl_file.put_line(file utl_file.file_type, buffer text, autoflush bool) +RETURNS bool +AS 'MODULE_PATHNAME','utl_file_put_line' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION utl_file.put_line(utl_file.file_type, text, bool) IS 'Puts data to specified file and append newline character'; + +CREATE FUNCTION utl_file.putf(file utl_file.file_type, format text, arg1 text, arg2 text, arg3 text, arg4 text, arg5 text) +RETURNS bool +AS 'MODULE_PATHNAME','utl_file_putf' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION utl_file.putf(utl_file.file_type, text, text, text, text, text, text) IS 'Puts formatted data to specified file'; + +CREATE FUNCTION utl_file.putf(file utl_file.file_type, format text, arg1 text, arg2 text, arg3 text, arg4 text) +RETURNS bool +AS $$SELECT utl_file.putf($1, $2, $3, $4, $5, $6, NULL); $$ +LANGUAGE SQL VOLATILE; +COMMENT ON FUNCTION utl_file.putf(utl_file.file_type, text, text, text, text, text) IS 'Puts formatted data to specified file'; + +CREATE FUNCTION utl_file.putf(file utl_file.file_type, format text, arg1 text, arg2 text, arg3 text) +RETURNS bool +AS $$SELECT utl_file.putf($1, $2, $3, $4, $5, NULL, NULL); $$ +LANGUAGE SQL VOLATILE; +COMMENT ON FUNCTION utl_file.putf(utl_file.file_type, text, text, text, text) IS 'Puts formatted data to specified file'; + +CREATE FUNCTION utl_file.putf(file utl_file.file_type, format text, arg1 text, arg2 text) +RETURNS bool +AS $$SELECT utl_file.putf($1, $2, $3, $4, NULL, NULL, NULL); $$ +LANGUAGE SQL VOLATILE; +COMMENT ON FUNCTION utl_file.putf(utl_file.file_type, text, text, text, text) IS 'Puts formatted data to specified file'; + +CREATE FUNCTION utl_file.putf(file utl_file.file_type, format text, arg1 text) +RETURNS bool +AS $$SELECT utl_file.putf($1, $2, $3, NULL, NULL, NULL, NULL); $$ +LANGUAGE SQL VOLATILE; +COMMENT ON FUNCTION utl_file.putf(utl_file.file_type, text, text) IS 'Puts formatted data to specified file'; + +CREATE FUNCTION utl_file.putf(file utl_file.file_type, format text) +RETURNS bool +AS $$SELECT utl_file.putf($1, $2, NULL, NULL, NULL, NULL, NULL); $$ +LANGUAGE SQL VOLATILE; +COMMENT ON FUNCTION utl_file.putf(utl_file.file_type, text) IS 'Puts formatted data to specified file'; + +CREATE FUNCTION utl_file.fflush(file utl_file.file_type) +RETURNS void +AS 'MODULE_PATHNAME','utl_file_fflush' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION utl_file.fflush(file utl_file.file_type) IS 'This procedure makes sure that all pending data for specified file is written physically out to a file'; + +CREATE FUNCTION utl_file.fclose(file utl_file.file_type) +RETURNS utl_file.file_type +AS 'MODULE_PATHNAME','utl_file_fclose' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION utl_file.fclose(utl_file.file_type) IS 'Close file'; + +CREATE FUNCTION utl_file.fclose_all() +RETURNS void +AS 'MODULE_PATHNAME','utl_file_fclose_all' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION utl_file.fclose_all() IS 'Close all open files.'; + +CREATE FUNCTION utl_file.fremove(location text, filename text) +RETURNS void +AS 'MODULE_PATHNAME','utl_file_fremove' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION utl_file.fremove(text, text) IS 'Remove file.'; + +CREATE FUNCTION utl_file.frename(location text, filename text, dest_dir text, dest_file text, overwrite boolean) +RETURNS void +AS 'MODULE_PATHNAME','utl_file_frename' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION utl_file.frename(text, text, text, text, boolean) IS 'Rename file.'; + +CREATE FUNCTION utl_file.frename(location text, filename text, dest_dir text, dest_file text) +RETURNS void +AS $$SELECT utl_file.frename($1, $2, $3, $4, false);$$ +LANGUAGE SQL VOLATILE; +COMMENT ON FUNCTION utl_file.frename(text, text, text, text) IS 'Rename file.'; + +CREATE FUNCTION utl_file.fcopy(src_location text, src_filename text, dest_location text, dest_filename text) +RETURNS void +AS 'MODULE_PATHNAME','utl_file_fcopy' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION utl_file.fcopy(text, text, text, text) IS 'Copy a text file.'; + +CREATE FUNCTION utl_file.fcopy(src_location text, src_filename text, dest_location text, dest_filename text, start_line integer) +RETURNS void +AS 'MODULE_PATHNAME','utl_file_fcopy' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION utl_file.fcopy(text, text, text, text, integer) IS 'Copy a text file.'; + +CREATE FUNCTION utl_file.fcopy(src_location text, src_filename text, dest_location text, dest_filename text, start_line integer, end_line integer) +RETURNS void +AS 'MODULE_PATHNAME','utl_file_fcopy' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION utl_file.fcopy(text, text, text, text, integer, integer) IS 'Copy a text file.'; + +CREATE FUNCTION utl_file.fgetattr(location text, filename text, OUT fexists boolean, OUT file_length bigint, OUT blocksize integer) +AS 'MODULE_PATHNAME','utl_file_fgetattr' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION utl_file.fgetattr(text, text) IS 'Get file attributes.'; + +CREATE FUNCTION utl_file.tmpdir() +RETURNS text +AS 'MODULE_PATHNAME','utl_file_tmpdir' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION utl_file.tmpdir() IS 'Get temp directory path.'; + +/* carry all safe directories */ +CREATE TABLE utl_file.utl_file_dir(dir text, dirname text unique); +REVOKE ALL ON utl_file.utl_file_dir FROM PUBLIC; + +/* allow only read on utl_file.utl_file_dir to unprivileged users */ +GRANT SELECT ON TABLE utl_file.utl_file_dir TO PUBLIC; + +-- dbms_assert + +CREATE SCHEMA dbms_assert; + +CREATE FUNCTION dbms_assert.enquote_literal(str varchar) +RETURNS varchar +AS 'MODULE_PATHNAME','dbms_assert_enquote_literal' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION dbms_assert.enquote_literal(varchar) IS 'Add leading and trailing quotes, verify that all single quotes are paired with adjacent single quotes'; + +CREATE FUNCTION dbms_assert.enquote_name(str varchar, loweralize boolean) +RETURNS varchar +AS 'MODULE_PATHNAME','dbms_assert_enquote_name' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION dbms_assert.enquote_name(varchar, boolean) IS 'Enclose name in double quotes'; + +CREATE FUNCTION dbms_assert.enquote_name(str varchar) +RETURNS varchar +AS 'SELECT dbms_assert.enquote_name($1, true)' +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION dbms_assert.enquote_name(varchar) IS 'Enclose name in double quotes'; + +CREATE FUNCTION dbms_assert.noop(str varchar) +RETURNS varchar +AS 'MODULE_PATHNAME','dbms_assert_noop' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION dbms_assert.noop(varchar) IS 'Returns value without any checking.'; + +CREATE FUNCTION dbms_assert.schema_name(str varchar) +RETURNS varchar +AS 'MODULE_PATHNAME','dbms_assert_schema_name' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION dbms_assert.schema_name(varchar) IS 'Verify input string is an existing schema name.'; + +CREATE FUNCTION dbms_assert.object_name(str varchar) +RETURNS varchar +AS 'MODULE_PATHNAME','dbms_assert_object_name' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION dbms_assert.object_name(varchar) IS 'Verify input string is an existing object name.'; + +CREATE FUNCTION dbms_assert.simple_sql_name(str varchar) +RETURNS varchar +AS 'MODULE_PATHNAME','dbms_assert_simple_sql_name' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION dbms_assert.object_name(varchar) IS 'Verify input string is an sql name.'; + +CREATE FUNCTION dbms_assert.qualified_sql_name(str varchar) +RETURNS varchar +AS 'MODULE_PATHNAME','dbms_assert_qualified_sql_name' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION dbms_assert.object_name(varchar) IS 'Verify input string is an qualified sql name.'; + +CREATE SCHEMA plunit; + +CREATE FUNCTION plunit.assert_true(condition boolean) +RETURNS void +AS 'MODULE_PATHNAME','plunit_assert_true' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION plunit.assert_true(condition boolean) IS 'Asserts that the condition is true'; + +CREATE FUNCTION plunit.assert_true(condition boolean, message varchar) +RETURNS void +AS 'MODULE_PATHNAME','plunit_assert_true_message' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION plunit.assert_true(condition boolean, message varchar) IS 'Asserts that the condition is true'; + +CREATE FUNCTION plunit.assert_false(condition boolean) +RETURNS void +AS 'MODULE_PATHNAME','plunit_assert_false' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION plunit.assert_false(condition boolean) IS 'Asserts that the condition is false'; + +CREATE FUNCTION plunit.assert_false(condition boolean, message varchar) +RETURNS void +AS 'MODULE_PATHNAME','plunit_assert_false_message' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION plunit.assert_false(condition boolean, message varchar) IS 'Asserts that the condition is false'; + +CREATE FUNCTION plunit.assert_null(actual anyelement) +RETURNS void +AS 'MODULE_PATHNAME','plunit_assert_null' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION plunit.assert_null(actual anyelement) IS 'Asserts that the actual is null'; + +CREATE FUNCTION plunit.assert_null(actual anyelement, message varchar) +RETURNS void +AS 'MODULE_PATHNAME','plunit_assert_null_message' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION plunit.assert_null(actual anyelement, message varchar) IS 'Asserts that the condition is null'; + +CREATE FUNCTION plunit.assert_not_null(actual anyelement) +RETURNS void +AS 'MODULE_PATHNAME','plunit_assert_not_null' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION plunit.assert_not_null(actual anyelement) IS 'Asserts that the actual is not null'; + +CREATE FUNCTION plunit.assert_not_null(actual anyelement, message varchar) +RETURNS void +AS 'MODULE_PATHNAME','plunit_assert_not_null_message' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION plunit.assert_not_null(actual anyelement, message varchar) IS 'Asserts that the condition is not null'; + +CREATE FUNCTION plunit.assert_equals(expected anyelement, actual anyelement) +RETURNS void +AS 'MODULE_PATHNAME','plunit_assert_equals' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION plunit.assert_equals(expected anyelement, actual anyelement) IS 'Asserts that expected and actual are equal'; + +CREATE FUNCTION plunit.assert_equals(expected anyelement, actual anyelement, message varchar) +RETURNS void +AS 'MODULE_PATHNAME','plunit_assert_equals_message' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION plunit.assert_equals(expected anyelement, actual anyelement, message varchar) IS 'Asserts that expected and actual are equal'; + +CREATE FUNCTION plunit.assert_equals(expected double precision, actual double precision, "range" double precision) +RETURNS void +AS 'MODULE_PATHNAME','plunit_assert_equals_range' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION plunit.assert_equals(expected double precision, actual double precision, "range" double precision) IS 'Asserts that expected and actual are equal'; + +CREATE FUNCTION plunit.assert_equals(expected double precision, actual double precision, "range" double precision, message varchar) +RETURNS void +AS 'MODULE_PATHNAME','plunit_assert_equals_range_message' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION plunit.assert_equals(expected double precision, actual double precision, "range" double precision, message varchar) IS 'Asserts that expected and actual are equal'; + +CREATE FUNCTION plunit.assert_not_equals(expected anyelement, actual anyelement) +RETURNS void +AS 'MODULE_PATHNAME','plunit_assert_not_equals' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION plunit.assert_not_equals(expected anyelement, actual anyelement) IS 'Asserts that expected and actual are equal'; + +CREATE FUNCTION plunit.assert_not_equals(expected anyelement, actual anyelement, message varchar) +RETURNS void +AS 'MODULE_PATHNAME','plunit_assert_not_equals_message' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION plunit.assert_not_equals(expected anyelement, actual anyelement, message varchar) IS 'Asserts that expected and actual are equal'; + +CREATE FUNCTION plunit.assert_not_equals(expected double precision, actual double precision, "range" double precision) +RETURNS void +AS 'MODULE_PATHNAME','plunit_assert_not_equals_range' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION plunit.assert_equals(expected double precision, actual double precision, "range" double precision) IS 'Asserts that expected and actual are equal'; + +CREATE FUNCTION plunit.assert_not_equals(expected double precision, actual double precision, "range" double precision, message varchar) +RETURNS void +AS 'MODULE_PATHNAME','plunit_assert_not_equals_range_message' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION plunit.assert_not_equals(expected double precision, actual double precision, "range" double precision, message varchar) IS 'Asserts that expected and actual are equal'; + +CREATE FUNCTION plunit.fail() +RETURNS void +AS 'MODULE_PATHNAME','plunit_fail' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION plunit.fail() IS 'Immediately fail.'; + +CREATE FUNCTION plunit.fail(message varchar) +RETURNS void +AS 'MODULE_PATHNAME','plunit_fail_message' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION plunit.fail(message varchar) IS 'Immediately fail.'; + +-- dbms_random +CREATE SCHEMA dbms_random; + +CREATE FUNCTION dbms_random.initialize(int) +RETURNS void +AS 'MODULE_PATHNAME','dbms_random_initialize' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION dbms_random.initialize(int) IS 'Initialize package with a seed value'; + +CREATE FUNCTION dbms_random.normal() +RETURNS double precision +AS 'MODULE_PATHNAME','dbms_random_normal' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION dbms_random.normal() IS 'Returns random numbers in a standard normal distribution'; + +CREATE FUNCTION dbms_random.random() +RETURNS integer +AS 'MODULE_PATHNAME','dbms_random_random' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION dbms_random.random() IS 'Generate Random Numeric Values'; + +CREATE FUNCTION dbms_random.seed(integer) +RETURNS void +AS 'MODULE_PATHNAME','dbms_random_seed_int' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION dbms_random.seed(int) IS 'Reset the seed value'; + +CREATE FUNCTION dbms_random.seed(text) +RETURNS void +AS 'MODULE_PATHNAME','dbms_random_seed_varchar' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION dbms_random.seed(text) IS 'Reset the seed value'; + +CREATE FUNCTION dbms_random.string(opt text, len int) +RETURNS text +AS 'MODULE_PATHNAME','dbms_random_string' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION dbms_random.string(text,int) IS 'Create Random Strings'; + +CREATE FUNCTION dbms_random.terminate() +RETURNS void +AS 'MODULE_PATHNAME','dbms_random_terminate' +LANGUAGE C IMMUTABLE; +COMMENT ON FUNCTION dbms_random.terminate() IS 'Terminate use of the Package'; + +CREATE FUNCTION dbms_random.value(low double precision, high double precision) +RETURNS double precision +AS 'MODULE_PATHNAME','dbms_random_value_range' +LANGUAGE C STRICT VOLATILE; +COMMENT ON FUNCTION dbms_random.value(double precision, double precision) IS 'Generate Random number x, where x is greater or equal to low and less then high'; + +CREATE FUNCTION dbms_random.value() +RETURNS double precision +AS 'MODULE_PATHNAME','dbms_random_value' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION dbms_random.value() IS 'Generate Random number x, where x is greater or equal to 0 and less then 1'; + +CREATE FUNCTION dump(text) +RETURNS varchar +AS 'MODULE_PATHNAME', 'orafce_dump' +LANGUAGE C; + +CREATE FUNCTION dump(text, integer) +RETURNS varchar +AS 'MODULE_PATHNAME', 'orafce_dump' +LANGUAGE C; + +CREATE FUNCTION utl_file.put_line(file utl_file.file_type, buffer anyelement) +RETURNS bool +AS $$SELECT utl_file.put_line($1, $2::text); $$ +LANGUAGE SQL VOLATILE; +COMMENT ON FUNCTION utl_file.put_line(utl_file.file_type, anyelement) IS 'Puts data to specified file and append newline character'; + +CREATE FUNCTION utl_file.put_line(file utl_file.file_type, buffer anyelement, autoflush bool) +RETURNS bool +AS $$SELECT utl_file.put_line($1, $2::text, true); $$ +LANGUAGE SQL VOLATILE; +COMMENT ON FUNCTION utl_file.put_line(utl_file.file_type, anyelement, bool) IS 'Puts data to specified file and append newline character'; + +CREATE FUNCTION pg_catalog.listagg1_transfn(internal, text) +RETURNS internal +AS 'MODULE_PATHNAME','orafce_listagg1_transfn' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION pg_catalog.wm_concat_transfn(internal, text) +RETURNS internal +AS 'MODULE_PATHNAME','orafce_wm_concat_transfn' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION pg_catalog.listagg2_transfn(internal, text, text) +RETURNS internal +AS 'MODULE_PATHNAME','orafce_listagg2_transfn' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION pg_catalog.listagg_finalfn(internal) +RETURNS text +AS 'MODULE_PATHNAME','orafce_listagg_finalfn' +LANGUAGE C IMMUTABLE; + +CREATE AGGREGATE pg_catalog.listagg(text) ( + SFUNC=pg_catalog.listagg1_transfn, + STYPE=internal, + FINALFUNC=pg_catalog.listagg_finalfn +); + +/* + * Undocumented function wm_concat - removed from + * Oracle 12c. + */ +CREATE AGGREGATE pg_catalog.wm_concat(text) ( + SFUNC=pg_catalog.wm_concat_transfn, + STYPE=internal, + FINALFUNC=pg_catalog.listagg_finalfn +); + +CREATE AGGREGATE pg_catalog.listagg(text, text) ( + SFUNC=pg_catalog.listagg2_transfn, + STYPE=internal, + FINALFUNC=pg_catalog.listagg_finalfn +); + +CREATE FUNCTION pg_catalog.median4_transfn(internal, real) +RETURNS internal +AS 'MODULE_PATHNAME','orafce_median4_transfn' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION pg_catalog.median4_finalfn(internal) +RETURNS real +AS 'MODULE_PATHNAME','orafce_median4_finalfn' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION pg_catalog.median8_transfn(internal, double precision) +RETURNS internal +AS 'MODULE_PATHNAME','orafce_median8_transfn' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION pg_catalog.median8_finalfn(internal) +RETURNS double precision +AS 'MODULE_PATHNAME','orafce_median8_finalfn' +LANGUAGE C IMMUTABLE; + +CREATE AGGREGATE pg_catalog.median(real) ( + SFUNC=pg_catalog.median4_transfn, + STYPE=internal, + FINALFUNC=pg_catalog.median4_finalfn +); + +CREATE AGGREGATE pg_catalog.median(double precision) ( + SFUNC=pg_catalog.median8_transfn, + STYPE=internal, + FINALFUNC=pg_catalog.median8_finalfn +); + +-- oracle.varchar2 type support + +CREATE FUNCTION varchar2in(cstring,oid,integer) +RETURNS varchar2 +AS 'MODULE_PATHNAME','varchar2in' +LANGUAGE C +STRICT +IMMUTABLE; + +CREATE FUNCTION varchar2out(varchar2) +RETURNS CSTRING +AS 'MODULE_PATHNAME','varchar2out' +LANGUAGE C +STRICT +IMMUTABLE; + +CREATE FUNCTION varchar2_transform(internal) +RETURNS internal +AS 'MODULE_PATHNAME','orafce_varchar_transform' +LANGUAGE C +STRICT +IMMUTABLE; + +CREATE FUNCTION varchar2recv(internal,oid,integer) +RETURNS varchar2 +AS 'MODULE_PATHNAME','varchar2recv' +LANGUAGE C +STRICT +STABLE; + +CREATE FUNCTION varchar2send(varchar2) +RETURNS bytea +AS 'varcharsend' +LANGUAGE internal +STRICT +STABLE; + +CREATE FUNCTION varchar2typmodin(cstring[]) +RETURNS integer +AS 'varchartypmodin' +LANGUAGE internal +STRICT +IMMUTABLE; + +CREATE FUNCTION varchar2typmodout(integer) +RETURNS CSTRING +AS 'varchartypmodout' +LANGUAGE internal +STRICT +IMMUTABLE; + +CREATE FUNCTION varchar2(varchar2,integer,boolean) +RETURNS varchar2 +AS 'MODULE_PATHNAME','varchar2' +LANGUAGE C +STRICT +IMMUTABLE; + +/* CREATE TYPE */ +CREATE TYPE varchar2 ( +internallength = VARIABLE, +input = varchar2in, +output = varchar2out, +receive = varchar2recv, +send = varchar2send, +category = 'S', +typmod_in = varchar2typmodin, +typmod_out = varchar2typmodout, +collatable = true +); + +CREATE FUNCTION oracle.orafce_concat2(varchar2, varchar2) +RETURNS varchar2 +AS 'MODULE_PATHNAME','orafce_concat2' +LANGUAGE C STABLE; + +/* CREATE CAST */ +CREATE CAST (varchar2 AS text) +WITHOUT FUNCTION +AS IMPLICIT; + +CREATE CAST (text AS varchar2) +WITHOUT FUNCTION +AS IMPLICIT; + +CREATE CAST (varchar2 AS char) +WITHOUT FUNCTION +AS IMPLICIT; + +CREATE CAST (char AS varchar2) +WITHOUT FUNCTION +AS IMPLICIT; + +CREATE CAST (varchar2 AS varchar) +WITHOUT FUNCTION +AS IMPLICIT; + +CREATE CAST (varchar AS varchar2) +WITHOUT FUNCTION +AS IMPLICIT; + +CREATE CAST (varchar2 AS varchar2) +WITH FUNCTION varchar2(varchar2,integer,boolean) +AS IMPLICIT; + +CREATE CAST (varchar2 AS real) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (real AS varchar2) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (varchar2 AS double precision) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (double precision AS varchar2) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (varchar2 AS integer) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (integer AS varchar2) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (varchar2 AS smallint) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (smallint AS varchar2) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (varchar2 AS bigint) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (bigint AS varchar2) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (varchar2 AS numeric) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (numeric AS varchar2) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (varchar2 AS date) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (date AS varchar2) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (varchar2 AS timestamp) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (timestamp AS varchar2) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (varchar2 AS interval) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (interval AS varchar2) +WITH INOUT +AS IMPLICIT; + +do $$ +BEGIN + IF EXISTS(SELECT * FROM pg_settings WHERE name = 'server_version_num' AND setting::int >= 120000) THEN + -- TODO Uncomment this and remove error when supported. + -- ALTER FUNCTION varchar2(varchar2, integer, boolean) SUPPORT varchar2_transform; + RAISE EXCEPTION 'Should not get here; Feature not supported'; + ELSE + UPDATE pg_proc SET protransform= 'varchar2_transform'::regproc::oid WHERE proname='varchar2'; + + INSERT INTO pg_depend (classid, objid, objsubid, + refclassid, refobjid, refobjsubid, deptype) + VALUES('pg_proc'::regclass::oid, 'varchar2'::regproc::oid, 0, + 'pg_proc'::regclass::oid, 'varchar2_transform'::regproc::oid, 0, 'n'); + END IF; +END +$$; + +-- string functions for varchar2 type +-- these are 'byte' versions of corresponsing text/varchar functions + +CREATE OR REPLACE FUNCTION pg_catalog.substrb(varchar2, integer, integer) RETURNS varchar2 +AS 'bytea_substr' +LANGUAGE internal +STRICT IMMUTABLE; +COMMENT ON FUNCTION pg_catalog.substrb(varchar2, integer, integer) IS 'extracts specified number of bytes from the input varchar2 string starting at the specified byte position (1-based) and returns as a varchar2 string'; + +CREATE OR REPLACE FUNCTION pg_catalog.substrb(varchar2, integer) RETURNS varchar2 +AS 'bytea_substr_no_len' +LANGUAGE internal +STRICT IMMUTABLE; +COMMENT ON FUNCTION pg_catalog.substrb(varchar2, integer) IS 'extracts specified number of bytes from the input varchar2 string starting at the specified byte position (1-based) and returns as a varchar2 string'; + +CREATE OR REPLACE FUNCTION pg_catalog.lengthb(varchar2) RETURNS integer +AS 'byteaoctetlen' +LANGUAGE internal +STRICT IMMUTABLE; +COMMENT ON FUNCTION pg_catalog.lengthb(varchar2) IS 'returns byte length of the input varchar2 string'; + +CREATE OR REPLACE FUNCTION pg_catalog.strposb(varchar2, varchar2) RETURNS integer +AS 'byteapos' +LANGUAGE internal +STRICT IMMUTABLE; +COMMENT ON FUNCTION pg_catalog.strposb(varchar2, varchar2) IS 'returns the byte position of a specified string in the input varchar2 string'; + +-- oracle.nvarchar2 type support + +CREATE FUNCTION nvarchar2in(cstring,oid,integer) +RETURNS nvarchar2 +AS 'MODULE_PATHNAME','nvarchar2in' +LANGUAGE C +STRICT +IMMUTABLE; + +CREATE FUNCTION nvarchar2out(nvarchar2) +RETURNS CSTRING +AS 'MODULE_PATHNAME','nvarchar2out' +LANGUAGE C +STRICT +IMMUTABLE; + +CREATE FUNCTION nvarchar2_transform(internal) +RETURNS internal +AS 'MODULE_PATHNAME','orafce_varchar_transform' +LANGUAGE C +STRICT +IMMUTABLE; + +CREATE FUNCTION nvarchar2recv(internal,oid,integer) +RETURNS nvarchar2 +AS 'MODULE_PATHNAME','nvarchar2recv' +LANGUAGE C +STRICT +STABLE; + +CREATE FUNCTION nvarchar2send(nvarchar2) +RETURNS bytea +AS 'varcharsend' +LANGUAGE internal +STRICT +STABLE; + +CREATE FUNCTION nvarchar2typmodin(cstring[]) +RETURNS integer +AS 'varchartypmodin' +LANGUAGE internal +STRICT +IMMUTABLE; + +CREATE FUNCTION nvarchar2typmodout(integer) +RETURNS CSTRING +AS 'varchartypmodout' +LANGUAGE internal +STRICT +IMMUTABLE; + +CREATE FUNCTION nvarchar2(nvarchar2,integer,boolean) +RETURNS nvarchar2 +AS 'MODULE_PATHNAME','nvarchar2' +LANGUAGE C +STRICT +IMMUTABLE; + +/* CREATE TYPE */ +CREATE TYPE nvarchar2 ( +internallength = VARIABLE, +input = nvarchar2in, +output = nvarchar2out, +receive = nvarchar2recv, +send = nvarchar2send, +category = 'S', +typmod_in = nvarchar2typmodin, +typmod_out = nvarchar2typmodout, +collatable = true +); + +CREATE FUNCTION oracle.orafce_concat2(nvarchar2, nvarchar2) +RETURNS nvarchar2 +AS 'MODULE_PATHNAME','orafce_concat2' +LANGUAGE C IMMUTABLE; + +/* CREATE CAST */ +CREATE CAST (nvarchar2 AS text) +WITHOUT FUNCTION +AS IMPLICIT; + +CREATE CAST (text AS nvarchar2) +WITHOUT FUNCTION +AS IMPLICIT; + +CREATE CAST (nvarchar2 AS char) +WITHOUT FUNCTION +AS IMPLICIT; + +CREATE CAST (char AS nvarchar2) +WITHOUT FUNCTION +AS IMPLICIT; + +CREATE CAST (nvarchar2 AS varchar) +WITHOUT FUNCTION +AS IMPLICIT; + +CREATE CAST (varchar AS nvarchar2) +WITHOUT FUNCTION +AS IMPLICIT; + +CREATE CAST (nvarchar2 AS nvarchar2) +WITH FUNCTION nvarchar2(nvarchar2, integer, boolean) +AS IMPLICIT; + +CREATE CAST (nvarchar2 AS real) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (real AS nvarchar2) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (nvarchar2 AS double precision) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (double precision AS nvarchar2) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (nvarchar2 AS integer) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (integer AS nvarchar2) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (nvarchar2 AS smallint) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (smallint AS nvarchar2) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (nvarchar2 AS bigint) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (bigint AS nvarchar2) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (nvarchar2 AS numeric) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (numeric AS nvarchar2) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (nvarchar2 AS date) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (date AS nvarchar2) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (nvarchar2 AS timestamp) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (timestamp AS nvarchar2) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (nvarchar2 AS interval) +WITH INOUT +AS IMPLICIT; + +CREATE CAST (interval AS nvarchar2) +WITH INOUT +AS IMPLICIT; + +do $$ +BEGIN + IF EXISTS(SELECT * FROM pg_settings WHERE name = 'server_version_num' AND setting::int >= 120000) THEN + -- TODO Uncomment this and remove error when supported. + -- ALTER FUNCTION nvarchar2(nvarchar2, integer, boolean) SUPPORT nvarchar2_transform; + RAISE EXCEPTION 'Should not get here; Feature not supported'; + ELSE + UPDATE pg_proc SET protransform= 'nvarchar2_transform'::regproc::oid WHERE proname='nvarchar2'; + + INSERT INTO pg_depend (classid, objid, objsubid, + refclassid, refobjid, refobjsubid, deptype) + VALUES('pg_proc'::regclass::oid, 'nvarchar2'::regproc::oid, 0, + 'pg_proc'::regclass::oid, 'nvarchar2_transform'::regproc::oid, 0, 'n'); + END IF; +END +$$; + +/* + * Note - a procedure keyword is depraceted from PostgreSQL 11, but it used + * because older release doesn't know function. + * + */ +CREATE OPERATOR || (procedure = oracle.orafce_concat2, leftarg = varchar2, rightarg = varchar2); +CREATE OPERATOR || (procedure = oracle.orafce_concat2, leftarg = nvarchar2, rightarg = nvarchar2); + +/* PAD */ + +/* LPAD family */ + +/* Incompatibility #1: + * pg_catalog.lpad removes trailing blanks of CHAR arguments + * because of implicit cast to text + * + * Incompatibility #2: + * pg_catalog.lpad considers character length, NOT display length + * so, add functions to use custom C implementation of lpad as defined + * in charpad.c + */ +CREATE FUNCTION oracle.lpad(char, integer, char) +RETURNS text +AS 'MODULE_PATHNAME','orafce_lpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.lpad(char, integer, text) +RETURNS text +AS 'MODULE_PATHNAME','orafce_lpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.lpad(char, integer, varchar2) +RETURNS text +AS 'MODULE_PATHNAME','orafce_lpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.lpad(char, integer, nvarchar2) +RETURNS text +AS 'MODULE_PATHNAME','orafce_lpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.lpad(char, integer) +RETURNS text +AS $$ SELECT oracle.lpad($1, $2, ' '::text); $$ +LANGUAGE SQL +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.lpad(text, integer, char) +RETURNS text +AS 'MODULE_PATHNAME','orafce_lpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.lpad(varchar2, integer, char) +RETURNS text +AS 'MODULE_PATHNAME','orafce_lpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.lpad(nvarchar2, integer, char) +RETURNS text +AS 'MODULE_PATHNAME','orafce_lpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.lpad(text, integer, text) +RETURNS text +AS 'MODULE_PATHNAME','orafce_lpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.lpad(text, integer, varchar2) +RETURNS text +AS 'MODULE_PATHNAME','orafce_lpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.lpad(text, integer, nvarchar2) +RETURNS text +AS 'MODULE_PATHNAME','orafce_lpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.lpad(text, integer) +RETURNS text +AS $$ SELECT oracle.lpad($1, $2, ' '::text); $$ +LANGUAGE SQL +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.lpad(varchar2, integer, text) +RETURNS text +AS 'MODULE_PATHNAME','orafce_lpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.lpad(varchar2, integer, varchar2) +RETURNS text +AS 'MODULE_PATHNAME','orafce_lpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.lpad(varchar2, integer, nvarchar2) +RETURNS text +AS 'MODULE_PATHNAME','orafce_lpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.lpad(varchar2, integer) +RETURNS text +AS $$ SELECT oracle.lpad($1, $2, ' '::text); $$ +LANGUAGE SQL +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.lpad(nvarchar2, integer, text) +RETURNS text +AS 'MODULE_PATHNAME','orafce_lpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.lpad(nvarchar2, integer, varchar2) +RETURNS text +AS 'MODULE_PATHNAME','orafce_lpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.lpad(nvarchar2, integer, nvarchar2) +RETURNS text +AS 'MODULE_PATHNAME','orafce_lpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.lpad(nvarchar2, integer) +RETURNS text +AS $$ SELECT oracle.lpad($1, $2, ' '::text); $$ +LANGUAGE SQL +STRICT IMMUTABLE +; + +/* RPAD family */ + +/* Incompatibility #1: + * pg_catalog.rpad removes trailing blanks of CHAR arguments + * because of implicit cast to text + * + * Incompatibility #2: + * pg_catalog.rpad considers character length, NOT display length + * so, add functions to use custom C implementation of rpad as defined + * in charpad.c + */ +CREATE FUNCTION oracle.rpad(char, integer, char) +RETURNS text +AS 'MODULE_PATHNAME','orafce_rpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rpad(char, integer, text) +RETURNS text +AS 'MODULE_PATHNAME','orafce_rpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rpad(char, integer, varchar2) +RETURNS text +AS 'MODULE_PATHNAME','orafce_rpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rpad(char, integer, nvarchar2) +RETURNS text +AS 'MODULE_PATHNAME','orafce_rpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rpad(char, integer) +RETURNS text +AS $$ SELECT oracle.rpad($1, $2, ' '::text); $$ +LANGUAGE SQL +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rpad(text, integer, char) +RETURNS text +AS 'MODULE_PATHNAME','orafce_rpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rpad(varchar2, integer, char) +RETURNS text +AS 'MODULE_PATHNAME','orafce_rpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rpad(nvarchar2, integer, char) +RETURNS text +AS 'MODULE_PATHNAME','orafce_rpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rpad(text, integer, text) +RETURNS text +AS 'MODULE_PATHNAME','orafce_rpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rpad(text, integer, varchar2) +RETURNS text +AS 'MODULE_PATHNAME','orafce_rpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rpad(text, integer, nvarchar2) +RETURNS text +AS 'MODULE_PATHNAME','orafce_rpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rpad(text, integer) +RETURNS text +AS $$ SELECT oracle.rpad($1, $2, ' '::text); $$ +LANGUAGE SQL +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rpad(varchar2, integer, text) +RETURNS text +AS 'MODULE_PATHNAME','orafce_rpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rpad(varchar2, integer, varchar2) +RETURNS text +AS 'MODULE_PATHNAME','orafce_rpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rpad(varchar2, integer, nvarchar2) +RETURNS text +AS 'MODULE_PATHNAME','orafce_rpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rpad(varchar2, integer) +RETURNS text +AS $$ SELECT oracle.rpad($1, $2, ' '::text); $$ +LANGUAGE SQL +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rpad(nvarchar2, integer, text) +RETURNS text +AS 'MODULE_PATHNAME','orafce_rpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rpad(nvarchar2, integer, varchar2) +RETURNS text +AS 'MODULE_PATHNAME','orafce_rpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rpad(nvarchar2, integer, nvarchar2) +RETURNS text +AS 'MODULE_PATHNAME','orafce_rpad' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rpad(nvarchar2, integer) +RETURNS text +AS $$ SELECT oracle.rpad($1, $2, ' '::text); $$ +LANGUAGE SQL +STRICT IMMUTABLE +; + +/* TRIM */ + +/* Incompatibility #1: + * pg_catalog.ltrim, pg_catalog.rtrim and pg_catalog.btrim remove + * trailing blanks of CHAR arguments because of implicit cast to + * text + * + * Following re-definitions address this incompatbility so that + * trailing blanks of CHAR arguments are preserved and considered + * significant for the trimming process. + */ + +/* LTRIM family */ +CREATE FUNCTION oracle.ltrim(char, char) +RETURNS text +AS 'ltrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.ltrim(char, text) +RETURNS text +AS 'ltrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.ltrim(char, varchar2) +RETURNS text +AS 'ltrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.ltrim(char, nvarchar2) +RETURNS text +AS 'ltrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.ltrim(char) +RETURNS text +AS $$ SELECT oracle.ltrim($1, ' '::text) $$ +LANGUAGE SQL +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.ltrim(text, char) +RETURNS text +AS 'ltrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.ltrim(text, text) +RETURNS text +AS 'ltrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.ltrim(text, varchar2) +RETURNS text +AS 'ltrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.ltrim(text, nvarchar2) +RETURNS text +AS 'ltrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.ltrim(text) +RETURNS text +AS $$ SELECT oracle.ltrim($1, ' '::text) $$ +LANGUAGE SQL +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.ltrim(varchar2, char) +RETURNS text +AS 'ltrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.ltrim(varchar2, text) +RETURNS text +AS 'ltrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.ltrim(varchar2, varchar2) +RETURNS text +AS 'ltrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.ltrim(varchar2, nvarchar2) +RETURNS text +AS 'ltrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.ltrim(varchar2) +RETURNS text +AS $$ SELECT oracle.ltrim($1, ' '::text) $$ +LANGUAGE SQL +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.ltrim(nvarchar2, char) +RETURNS text +AS 'ltrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.ltrim(nvarchar2, text) +RETURNS text +AS 'ltrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.ltrim(nvarchar2, varchar2) +RETURNS text +AS 'ltrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.ltrim(nvarchar2, nvarchar2) +RETURNS text +AS 'ltrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.ltrim(nvarchar2) +RETURNS text +AS $$ SELECT oracle.ltrim($1, ' '::text) $$ +LANGUAGE SQL +STRICT IMMUTABLE +; + +/* RTRIM family */ +CREATE FUNCTION oracle.rtrim(char, char) +RETURNS text +AS 'rtrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rtrim(char, text) +RETURNS text +AS 'rtrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rtrim(char, varchar2) +RETURNS text +AS 'rtrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rtrim(char, nvarchar2) +RETURNS text +AS 'rtrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rtrim(char) +RETURNS text +AS $$ SELECT oracle.rtrim($1, ' '::text) $$ +LANGUAGE SQL +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rtrim(text, char) +RETURNS text +AS 'rtrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rtrim(text, text) +RETURNS text +AS 'rtrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rtrim(text, varchar2) +RETURNS text +AS 'rtrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rtrim(text, nvarchar2) +RETURNS text +AS 'rtrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rtrim(text) +RETURNS text +AS $$ SELECT oracle.rtrim($1, ' '::text) $$ +LANGUAGE SQL +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rtrim(varchar2, char) +RETURNS text +AS 'rtrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rtrim(varchar2, text) +RETURNS text +AS 'rtrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rtrim(varchar2, varchar2) +RETURNS text +AS 'rtrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rtrim(varchar2, nvarchar2) +RETURNS text +AS 'rtrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rtrim(varchar2) +RETURNS text +AS $$ SELECT oracle.rtrim($1, ' '::text) $$ +LANGUAGE SQL +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rtrim(nvarchar2, char) +RETURNS text +AS 'rtrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rtrim(nvarchar2, text) +RETURNS text +AS 'rtrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rtrim(nvarchar2, varchar2) +RETURNS text +AS 'rtrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rtrim(nvarchar2, nvarchar2) +RETURNS text +AS 'rtrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.rtrim(nvarchar2) +RETURNS text +AS $$ SELECT oracle.rtrim($1, ' '::text) $$ +LANGUAGE SQL +STRICT IMMUTABLE +; + +/* BTRIM family */ +CREATE FUNCTION oracle.btrim(char, char) +RETURNS text +AS 'btrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.btrim(char, text) +RETURNS text +AS 'btrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.btrim(char, varchar2) +RETURNS text +AS 'btrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.btrim(char, nvarchar2) +RETURNS text +AS 'btrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.btrim(char) +RETURNS text +AS $$ SELECT oracle.btrim($1, ' '::text) $$ +LANGUAGE SQL +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.btrim(text, char) +RETURNS text +AS 'btrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.btrim(text, text) +RETURNS text +AS 'btrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.btrim(text, varchar2) +RETURNS text +AS 'btrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.btrim(text, nvarchar2) +RETURNS text +AS 'btrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.btrim(text) +RETURNS text +AS $$ SELECT oracle.btrim($1, ' '::text) $$ +LANGUAGE SQL +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.btrim(varchar2, char) +RETURNS text +AS 'btrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.btrim(varchar2, text) +RETURNS text +AS 'btrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.btrim(varchar2, varchar2) +RETURNS text +AS 'btrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.btrim(varchar2, nvarchar2) +RETURNS text +AS 'btrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.btrim(varchar2) +RETURNS text +AS $$ SELECT oracle.btrim($1, ' '::text) $$ +LANGUAGE SQL +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.btrim(nvarchar2, char) +RETURNS text +AS 'btrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.btrim(nvarchar2, text) +RETURNS text +AS 'btrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.btrim(nvarchar2, varchar2) +RETURNS text +AS 'btrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.btrim(nvarchar2, nvarchar2) +RETURNS text +AS 'btrim' +LANGUAGE internal +STRICT IMMUTABLE +; + +CREATE FUNCTION oracle.btrim(nvarchar2) +RETURNS text +AS $$ SELECT oracle.btrim($1, ' '::text) $$ +LANGUAGE SQL +STRICT IMMUTABLE +; + +/* LENGTH */ +CREATE FUNCTION oracle.length(char) +RETURNS integer +AS 'MODULE_PATHNAME','orafce_bpcharlen' +LANGUAGE 'c' +STRICT IMMUTABLE +; + +GRANT USAGE ON SCHEMA dbms_pipe TO PUBLIC; +GRANT USAGE ON SCHEMA dbms_alert TO PUBLIC; +GRANT USAGE ON SCHEMA plvdate TO PUBLIC; +GRANT USAGE ON SCHEMA plvstr TO PUBLIC; +GRANT USAGE ON SCHEMA plvchr TO PUBLIC; +GRANT USAGE ON SCHEMA dbms_output TO PUBLIC; +GRANT USAGE ON SCHEMA plvsubst TO PUBLIC; +GRANT SELECT ON dbms_pipe.db_pipes to PUBLIC; +GRANT USAGE ON SCHEMA dbms_utility TO PUBLIC; +GRANT USAGE ON SCHEMA plvlex TO PUBLIC; +GRANT USAGE ON SCHEMA utl_file TO PUBLIC; +GRANT USAGE ON SCHEMA dbms_assert TO PUBLIC; +GRANT USAGE ON SCHEMA dbms_random TO PUBLIC; +GRANT USAGE ON SCHEMA oracle TO PUBLIC; +GRANT USAGE ON SCHEMA plunit TO PUBLIC; + +/* orafce 3.3. related changes */ +ALTER FUNCTION dbms_assert.enquote_name ( character varying ) STRICT; +ALTER FUNCTION dbms_assert.enquote_name ( character varying, boolean ) STRICT; +ALTER FUNCTION dbms_assert.noop ( character varying ) STRICT; + +CREATE FUNCTION pg_catalog.trunc(value timestamp without time zone, fmt text) +RETURNS timestamp without time zone +AS 'MODULE_PATHNAME', 'ora_timestamp_trunc' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.trunc(timestamp without time zone, text) IS 'truncate date according to the specified format'; + +CREATE FUNCTION pg_catalog.round(value timestamp without time zone, fmt text) +RETURNS timestamp without time zone +AS 'MODULE_PATHNAME','ora_timestamp_round' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.round(timestamp with time zone, text) IS 'round dates according to the specified format'; + +CREATE FUNCTION pg_catalog.round(value timestamp without time zone) +RETURNS timestamp without time zone +AS $$ SELECT pg_catalog.round($1, 'DDD'); $$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.round(timestamp without time zone) IS 'will round dates according to the specified format'; + +CREATE FUNCTION pg_catalog.trunc(value timestamp without time zone) +RETURNS timestamp without time zone +AS $$ SELECT pg_catalog.trunc($1, 'DDD'); $$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.trunc(timestamp without time zone) IS 'truncate date according to the specified format'; + +CREATE OR REPLACE FUNCTION oracle.round(double precision, int) +RETURNS numeric +AS $$SELECT pg_catalog.round($1::numeric, $2)$$ +LANGUAGE sql IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION oracle.trunc(double precision, int) +RETURNS numeric +AS $$SELECT pg_catalog.trunc($1::numeric, $2)$$ +LANGUAGE sql IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION oracle.round(float4, int) +RETURNS numeric +AS $$SELECT pg_catalog.round($1::numeric, $2)$$ +LANGUAGE sql IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION oracle.trunc(float4, int) +RETURNS numeric +AS $$SELECT pg_catalog.trunc($1::numeric, $2)$$ +LANGUAGE sql IMMUTABLE STRICT; + + +CREATE FUNCTION oracle.get_major_version() +RETURNS text +AS 'MODULE_PATHNAME','ora_get_major_version' +LANGUAGE 'c' STRICT IMMUTABLE; + +CREATE FUNCTION oracle.get_major_version_num() +RETURNS text +AS 'MODULE_PATHNAME','ora_get_major_version_num' +LANGUAGE 'c' STRICT IMMUTABLE; + +CREATE FUNCTION oracle.get_full_version_num() +RETURNS text +AS 'MODULE_PATHNAME','ora_get_full_version_num' +LANGUAGE 'c' STRICT IMMUTABLE; + +CREATE FUNCTION oracle.get_platform() +RETURNS text +AS 'MODULE_PATHNAME','ora_get_platform' +LANGUAGE 'c' STRICT IMMUTABLE; + +CREATE FUNCTION oracle.get_status() +RETURNS text +AS 'MODULE_PATHNAME','ora_get_status' +LANGUAGE 'c' STRICT IMMUTABLE; + +-- Oracle system views +create view oracle.user_tab_columns as + select table_name, + column_name, + data_type, + coalesce(character_maximum_length, numeric_precision) AS data_length, + numeric_precision AS data_precision, + numeric_scale AS data_scale, + is_nullable AS nullable, + ordinal_position AS column_id, + is_updatable AS data_upgraded, + table_schema + from information_schema.columns; + +create view oracle.user_tables as + select table_name + from information_schema.tables + where table_type = 'BASE TABLE'; + +create view oracle.user_cons_columns as + select constraint_name, column_name, table_name + from information_schema.constraint_column_usage ; + +create view oracle.user_constraints as + select conname as constraint_name, + conindid::regclass as index_name, + case contype when 'p' then 'P' when 'f' then 'R' end as constraint_type, + conrelid::regclass as table_name, + case contype when 'f' then (select conname + from pg_constraint c2 + where contype = 'p' and c2.conindid = c1.conindid) + end as r_constraint_name + from pg_constraint c1, pg_class + where conrelid = pg_class.oid; + +create view oracle.product_component_version as + select oracle.get_major_version() as product, + oracle.get_full_version_num() as version, + oracle.get_platform() || ' ' || oracle.get_status() as status + union all + select extname, + case when extname = 'plpgsql' then oracle.get_full_version_num() else extversion end, + oracle.get_platform() || ' ' || oracle.get_status() + from pg_extension; + +create view oracle.user_objects as + select relname as object_name, + null::text as subject_name, + c.oid as object_id, + case relkind when 'r' then 'TABLE' + when 'i' then 'INDEX' + when 'S' then 'SEQUENCE' + when 'v' then 'VIEW' + when 'm' then 'VIEW' + when 'f' then 'FOREIGN TABLE' end as object_type, + null::timestamp(0) as created, + null::timestamp(0) as last_ddl_time, + case when relkind = 'i' then (select case when indisvalid then 'VALID' else 'INVALID' end + from pg_index + where indexrelid = c.oid) + else case when relispopulated then 'VALID' else 'INVALID' end end as status, + relnamespace as namespace + from pg_class c join pg_namespace n on c.relnamespace = n.oid + where relkind not in ('t','c') + and nspname not in ('pg_toast','pg_catalog','information_schema') + union all + select tgname, null, t.oid, 'TRIGGER',null, null,'VALID', relnamespace + from pg_trigger t join pg_class c on t.tgrelid = c.oid + where not tgisinternal + union all + select proname, null, p.oid, 'FUNCTION', null, null, 'VALID', pronamespace + from pg_proc p join pg_namespace n on p.pronamespace = n.oid + where nspname not in ('pg_toast','pg_catalog','information_schema') order by 1; + +create view oracle.user_procedures as + select proname as object_name + from pg_proc p join pg_namespace n on p.pronamespace = n.oid + and nspname <> 'pg_catalog'; + +create view oracle.user_source as + select row_number() over (partition by oid) as line, * + from ( select oid, unnest(string_to_array(prosrc, e'\n')) as text, + proname as name, 'FUNCTION'::text as type + from pg_proc) s; + +create view oracle.user_views + as select c.relname as view_name, + pg_catalog.pg_get_userbyid(c.relowner) as owner +from pg_catalog.pg_class c + left join pg_catalog.pg_namespace n on n.oid = c.relnamespace +where c.relkind in ('v','') + and n.nspname <> 'pg_catalog' + and n.nspname <> 'information_schema' + and n.nspname !~ '^pg_toast' + and pg_catalog.pg_table_is_visible(c.oid); + +create view oracle.user_ind_columns as + select attname as column_name, c1.relname as index_name, c2.relname as table_name + from (select unnest(indkey) attno, indexrelid, indrelid from pg_index) s + join pg_attribute on attno = attnum and attrelid = indrelid + join pg_class c1 on indexrelid = c1.oid + join pg_class c2 on indrelid = c2.oid + join pg_namespace n on c2.relnamespace = n.oid + where attno > 0 and nspname not in ('pg_catalog','information_schema'); + +CREATE VIEW oracle.dba_segments AS +SELECT + pg_namespace.nspname AS owner, + pg_class.relname AS segment_name, + CASE + WHEN pg_class.relkind = 'r' THEN CAST( 'TABLE' AS VARCHAR( 18 ) ) + WHEN pg_class.relkind = 'i' THEN CAST( 'INDEX' AS VARCHAR( 18 ) ) + WHEN pg_class.relkind = 'f' THEN CAST( 'FOREIGN TABLE' AS VARCHAR( 18 ) ) + WHEN pg_class.relkind = 'S' THEN CAST( 'SEQUENCE' AS VARCHAR( 18 ) ) + WHEN pg_class.relkind = 's' THEN CAST( 'SPECIAL' AS VARCHAR( 18 ) ) + WHEN pg_class.relkind = 't' THEN CAST( 'TOAST TABLE' AS VARCHAR( 18 ) ) + WHEN pg_class.relkind = 'v' THEN CAST( 'VIEW' AS VARCHAR( 18 ) ) + ELSE CAST( pg_class.relkind AS VARCHAR( 18 ) ) + END AS segment_type, + spcname AS tablespace_name, + relfilenode AS header_file, + NULL::oid AS header_block, + pg_relation_size( pg_class.oid ) AS bytes, + relpages AS blocks +FROM + pg_class + INNER JOIN pg_namespace + ON pg_class.relnamespace = pg_namespace.oid + LEFT OUTER JOIN pg_tablespace + ON pg_class.reltablespace = pg_tablespace.oid +WHERE + pg_class.relkind not in ('f','S','v'); + +-- Oracle dirty functions +CREATE OR REPLACE FUNCTION oracle.lpad(int, int, int) +RETURNS text AS $$ +SELECT pg_catalog.lpad($1::text,$2,$3::text) +$$ LANGUAGE sql IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION oracle.lpad(bigint, int, int) +RETURNS text AS $$ +SELECT pg_catalog.lpad($1::text,$2,$3::text) +$$ LANGUAGE sql IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION oracle.lpad(smallint, int, int) +RETURNS text AS $$ +SELECT pg_catalog.lpad($1::text,$2,$3::text) +$$ LANGUAGE sql IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION oracle.lpad(numeric, int, int) +RETURNS text AS $$ +SELECT pg_catalog.lpad($1::text,$2,$3::text) +$$ LANGUAGE sql IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION oracle.nvl(bigint, int) +RETURNS bigint AS $$ +SELECT coalesce($1, $2) +$$ LANGUAGE sql IMMUTABLE; + +CREATE OR REPLACE FUNCTION oracle.nvl(numeric, int) +RETURNS numeric AS $$ +SELECT coalesce($1, $2) +$$ LANGUAGE sql IMMUTABLE; + +CREATE OR REPLACE FUNCTION oracle.nvl(int, int) +RETURNS int AS $$ +SELECT coalesce($1, $2) +$$ LANGUAGE sql IMMUTABLE; + +CREATE OR REPLACE FUNCTION oracle.numtodsinterval(double precision, text) +RETURNS interval AS $$ + SELECT $1 * ('1' || $2)::interval +$$ LANGUAGE sql IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION oracle.replace_empty_strings() +RETURNS TRIGGER +AS 'MODULE_PATHNAME','orafce_replace_empty_strings' +LANGUAGE 'c'; + +CREATE OR REPLACE FUNCTION oracle.replace_null_strings() +RETURNS TRIGGER +AS 'MODULE_PATHNAME','orafce_replace_null_strings' +LANGUAGE 'c'; + +CREATE OR REPLACE FUNCTION oracle.unistr(text) +RETURNS text +AS 'MODULE_PATHNAME','orafce_unistr' +LANGUAGE 'c'; diff --git a/src/postgres/third-party-extensions/orafce/orafce--3.2--3.3.sql b/src/postgres/third-party-extensions/orafce/orafce--3.2--3.3.sql new file mode 100644 index 000000000000..b3a732fb7150 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/orafce--3.2--3.3.sql @@ -0,0 +1,71 @@ +ALTER FUNCTION dbms_assert.enquote_name ( character varying ) STRICT; +ALTER FUNCTION dbms_assert.enquote_name ( character varying, boolean ) STRICT; +ALTER FUNCTION dbms_assert.noop ( character varying ) STRICT; + +CREATE FUNCTION pg_catalog.trunc(value timestamp without time zone, fmt text) +RETURNS timestamp without time zone +AS 'MODULE_PATHNAME', 'ora_timestamp_trunc' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.trunc(timestamp without time zone, text) IS 'truncate date according to the specified format'; + +CREATE FUNCTION pg_catalog.round(value timestamp without time zone, fmt text) +RETURNS timestamp without time zone +AS 'MODULE_PATHNAME','ora_timestamp_round' +LANGUAGE C IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.round(timestamp with time zone, text) IS 'round dates according to the specified format'; + +CREATE FUNCTION pg_catalog.round(value timestamp without time zone) +RETURNS timestamp without time zone +AS $$ SELECT pg_catalog.round($1, 'DDD'); $$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.round(timestamp without time zone) IS 'will round dates according to the specified format'; + +CREATE FUNCTION pg_catalog.trunc(value timestamp without time zone) +RETURNS timestamp without time zone +AS $$ SELECT pg_catalog.trunc($1, 'DDD'); $$ +LANGUAGE SQL IMMUTABLE STRICT; +COMMENT ON FUNCTION pg_catalog.trunc(timestamp without time zone) IS 'truncate date according to the specified format'; + +CREATE FUNCTION plvdate.use_great_friday(bool) +RETURNS void +AS 'MODULE_PATHNAME','plvdate_use_great_friday' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION plvdate.use_great_friday(bool) IS 'Great Friday will be holiday'; + +CREATE FUNCTION plvdate.use_great_friday() +RETURNS bool +AS $$SELECT plvdate.use_great_friday(true); SELECT NULL::boolean;$$ +LANGUAGE SQL VOLATILE STRICT; +COMMENT ON FUNCTION plvdate.use_great_friday() IS 'Great Friday will be holiday'; + +CREATE FUNCTION plvdate.unuse_great_friday() +RETURNS bool +AS $$SELECT plvdate.use_great_friday(false); SELECT NULL::boolean;$$ +LANGUAGE SQL VOLATILE STRICT; +COMMENT ON FUNCTION plvdate.unuse_great_friday() IS 'Great Friday will not be holiday'; + +CREATE FUNCTION plvdate.using_great_friday() +RETURNS bool +AS 'MODULE_PATHNAME','plvdate_using_great_friday' +LANGUAGE C VOLATILE STRICT; +COMMENT ON FUNCTION plvdate.using_great_friday() IS 'Use Great Friday?'; + +CREATE OR REPLACE FUNCTION oracle.round(double precision, int) +RETURNS numeric +AS $$SELECT pg_catalog.round($1::numeric, $2)$$ +LANGUAGE sql; + +CREATE OR REPLACE FUNCTION oracle.trunc(double precision, int) +RETURNS numeric +AS $$SELECT pg_catalog.trunc($1::numeric, $2)$$ +LANGUAGE sql; + +CREATE OR REPLACE FUNCTION oracle.round(float, int) +RETURNS numeric +AS $$SELECT pg_catalog.round($1::numeric, $2)$$ +LANGUAGE sql; + +CREATE OR REPLACE FUNCTION oracle.trunc(float, int) +RETURNS numeric +AS $$SELECT pg_catalog.trunc($1::numeric, $2)$$ +LANGUAGE sql; diff --git a/src/postgres/third-party-extensions/orafce/orafce--3.3--3.4.sql b/src/postgres/third-party-extensions/orafce/orafce--3.3--3.4.sql new file mode 100644 index 000000000000..948c2ec5ea80 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/orafce--3.3--3.4.sql @@ -0,0 +1,14 @@ +/* + * Undocumented function wm_concat - removed from + * Oracle 12c. + */ +CREATE FUNCTION pg_catalog.wm_concat_transfn(internal, text) +RETURNS internal +AS 'MODULE_PATHNAME','orafce_wm_concat_transfn' +LANGUAGE C IMMUTABLE; + +CREATE AGGREGATE pg_catalog.wm_concat(text) ( + SFUNC=pg_catalog.wm_concat_transfn, + STYPE=internal, + FINALFUNC=pg_catalog.listagg_finalfn +); diff --git a/src/postgres/third-party-extensions/orafce/orafce--3.4--3.5.sql b/src/postgres/third-party-extensions/orafce/orafce--3.4--3.5.sql new file mode 100644 index 000000000000..e035a69a3aad --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/orafce--3.4--3.5.sql @@ -0,0 +1,7 @@ +update pg_proc set provolatile = 'i' + from pg_namespace + where pg_namespace.oid = pronamespace and nspname = 'oracle' + and proname in ('trunc','round','length','btrim','ltrim','rtrim','lpad','rpad'); + +update pg_type set typcollation = 100 + where typname in ('varchar2', 'nvarchar2'); diff --git a/src/postgres/third-party-extensions/orafce/orafce--3.5--3.6.sql b/src/postgres/third-party-extensions/orafce/orafce--3.5--3.6.sql new file mode 100644 index 000000000000..50ea3a9d0956 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/orafce--3.5--3.6.sql @@ -0,0 +1,188 @@ +CREATE FUNCTION oracle.get_major_version() +RETURNS text +AS 'MODULE_PATHNAME','ora_get_major_version' +LANGUAGE 'c' STRICT IMMUTABLE; + +CREATE FUNCTION oracle.get_major_version_num() +RETURNS text +AS 'MODULE_PATHNAME','ora_get_major_version_num' +LANGUAGE 'c' STRICT IMMUTABLE; + +CREATE FUNCTION oracle.get_full_version_num() +RETURNS text +AS 'MODULE_PATHNAME','ora_get_full_version_num' +LANGUAGE 'c' STRICT IMMUTABLE; + +CREATE FUNCTION oracle.get_platform() +RETURNS text +AS 'MODULE_PATHNAME','ora_get_platform' +LANGUAGE 'c' STRICT IMMUTABLE; + +CREATE FUNCTION oracle.get_status() +RETURNS text +AS 'MODULE_PATHNAME','ora_get_status' +LANGUAGE 'c' STRICT IMMUTABLE; + +-- Oracle system views +create view oracle.user_tab_columns as + select table_name, + column_name, + data_type, + coalesce(character_maximum_length, numeric_precision) AS data_length, + numeric_precision AS data_precision, + numeric_scale AS data_scale, + is_nullable AS nullable, + ordinal_position AS column_id, + is_updatable AS data_upgraded, + table_schema + from information_schema.columns; + +create view oracle.user_tables as + select table_name + from information_schema.tables + where table_type = 'BASE TABLE'; + +create view oracle.user_cons_columns as + select constraint_name, column_name, table_name + from information_schema.constraint_column_usage ; + +create view oracle.user_constraints as + select conname as constraint_name, + conindid::regclass as index_name, + case contype when 'p' then 'P' when 'f' then 'R' end as constraint_type, + relname as table_name, + case contype when 'f' then (select conname + from pg_constraint c2 + where contype = 'p' and c2.conindid = c1.conindid) + end as r_constraint_name + from pg_constraint c1, pg_class + where conrelid = pg_class.oid; + +create view oracle.product_component_version as + select oracle.get_major_version() as product, + oracle.get_full_version_num() as version, + oracle.get_platform() || ' ' || oracle.get_status() as status + union all + select extname, + case when extname = 'plpgsql' then oracle.get_full_version_num() else extversion end, + oracle.get_platform() || ' ' || oracle.get_status() + from pg_extension; + +create view oracle.user_objects as + select relname as object_name, + null::text as subject_name, + c.oid as object_id, + case relkind when 'r' then 'TABLE' + when 'i' then 'INDEX' + when 'S' then 'SEQUENCE' + when 'v' then 'VIEW' + when 'm' then 'VIEW' + when 'f' then 'FOREIGN TABLE' end as object_type, + null::timestamp(0) as created, + null::timestamp(0) as last_ddl_time, + case when relkind = 'i' then (select case when indisvalid then 'VALID' else 'INVALID' end + from pg_index + where indexrelid = c.oid) + else case when relispopulated then 'VALID' else 'INVALID' end end as status, + relnamespace as namespace + from pg_class c join pg_namespace n on c.relnamespace = n.oid + where relkind not in ('t','c') + and nspname not in ('pg_toast','pg_catalog','information_schema') + union all + select tgname, null, t.oid, 'TRIGGER',null, null,'VALID', relnamespace + from pg_trigger t join pg_class c on t.tgrelid = c.oid + where not tgisinternal + union all + select proname, null, p.oid, 'FUNCTION', null, null, 'VALID', pronamespace + from pg_proc p join pg_namespace n on p.pronamespace = n.oid + where nspname not in ('pg_toast','pg_catalog','information_schema') order by 1; + +create view oracle.user_procedures as + select proname as object_name + from pg_proc p join pg_namespace n on p.pronamespace = n.oid + and nspname <> 'pg_catalog'; + +create view oracle.user_source as + select row_number() over (partition by oid) as line, * + from ( select oid, unnest(string_to_array(prosrc, e'\n')) as text, + proname as name, 'FUNCTION'::text as type + from pg_proc) s; + +create view oracle.user_views + as select c.relname as view_name, + pg_catalog.pg_get_userbyid(c.relowner) as owner +from pg_catalog.pg_class c + left join pg_catalog.pg_namespace n on n.oid = c.relnamespace +where c.relkind in ('v','') + and n.nspname <> 'pg_catalog' + and n.nspname <> 'information_schema' + and n.nspname !~ '^pg_toast' + and pg_catalog.pg_table_is_visible(c.oid); + +create view oracle.user_ind_columns as + select attname as column_name, c1.relname as index_name, c2.relname as table_name + from (select unnest(indkey) attno, indexrelid, indrelid from pg_index) s + join pg_attribute on attno = attnum and attrelid = indrelid + join pg_class c1 on indexrelid = c1.oid + join pg_class c2 on indrelid = c2.oid + join pg_namespace n on c2.relnamespace = n.oid + where attno > 0 and nspname not in ('pg_catalog','information_schema'); + +CREATE VIEW oracle.dba_segments AS +SELECT + pg_namespace.nspname AS owner, + pg_class.relname AS segment_name, + CASE + WHEN pg_class.relkind = 'r' THEN CAST( 'TABLE' AS VARCHAR( 18 ) ) + WHEN pg_class.relkind = 'i' THEN CAST( 'INDEX' AS VARCHAR( 18 ) ) + WHEN pg_class.relkind = 'f' THEN CAST( 'FOREIGN TABLE' AS VARCHAR( 18 ) ) + WHEN pg_class.relkind = 'S' THEN CAST( 'SEQUENCE' AS VARCHAR( 18 ) ) + WHEN pg_class.relkind = 's' THEN CAST( 'SPECIAL' AS VARCHAR( 18 ) ) + WHEN pg_class.relkind = 't' THEN CAST( 'TOAST TABLE' AS VARCHAR( 18 ) ) + WHEN pg_class.relkind = 'v' THEN CAST( 'VIEW' AS VARCHAR( 18 ) ) + ELSE CAST( pg_class.relkind AS VARCHAR( 18 ) ) + END AS segment_type, + spcname AS tablespace_name, + relfilenode AS header_file, + NULL::oid AS header_block, + pg_relation_size( pg_class.oid ) AS bytes, + relpages AS blocks +FROM + pg_class + INNER JOIN pg_namespace + ON pg_class.relnamespace = pg_namespace.oid + LEFT OUTER JOIN pg_tablespace + ON pg_class.reltablespace = pg_tablespace.oid +WHERE + pg_class.relkind not in ('f','S','v'); + +-- Oracle dirty functions +CREATE OR REPLACE FUNCTION oracle.lpad(int, int, int) +RETURNS text AS $$ +SELECT pg_catalog.lpad($1::text,$2,$3::text) +$$ LANGUAGE sql IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION oracle.lpad(bigint, int, int) +RETURNS text AS $$ +SELECT pg_catalog.lpad($1::text,$2,$3::text) +$$ LANGUAGE sql IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION oracle.lpad(smallint, int, int) +RETURNS text AS $$ +SELECT pg_catalog.lpad($1::text,$2,$3::text) +$$ LANGUAGE sql IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION oracle.lpad(numeric, int, int) +RETURNS text AS $$ +SELECT pg_catalog.lpad($1::text,$2,$3::text) +$$ LANGUAGE sql IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION oracle.nvl(bigint, int) +RETURNS bigint AS $$ +SELECT coalesce($1, $2) +$$ LANGUAGE sql IMMUTABLE; + +CREATE OR REPLACE FUNCTION oracle.nvl(numeric, int) +RETURNS numeric AS $$ +SELECT coalesce($1, $2) +$$ LANGUAGE sql IMMUTABLE; diff --git a/src/postgres/third-party-extensions/orafce/orafce--3.6--3.7.sql b/src/postgres/third-party-extensions/orafce/orafce--3.6--3.7.sql new file mode 100644 index 000000000000..e0339fae5906 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/orafce--3.6--3.7.sql @@ -0,0 +1,57 @@ +-- bugfixes +GRANT USAGE ON SCHEMA oracle TO PUBLIC; +GRANT USAGE ON SCHEMA plunit TO PUBLIC; + +CREATE OR REPLACE FUNCTION oracle.round(float4, int) +RETURNS numeric +AS $$SELECT pg_catalog.round($1::numeric, $2)$$ +LANGUAGE sql IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION oracle.trunc(float4, int) +RETURNS numeric +AS $$SELECT pg_catalog.trunc($1::numeric, $2)$$ +LANGUAGE sql IMMUTABLE STRICT; + +COMMENT ON FUNCTION oracle.sessiontimezone() IS 'Ruturns session time zone'; +COMMENT ON FUNCTION oracle.dbtimezone() IS 'Ruturns server time zone (orafce.timezone)'; + +CREATE OR REPLACE FUNCTION oracle.nvl(bigint, int) +RETURNS bigint AS $$ +SELECT coalesce($1, $2) +$$ LANGUAGE sql IMMUTABLE; + +CREATE OR REPLACE FUNCTION oracle.nvl(numeric, int) +RETURNS numeric AS $$ +SELECT coalesce($1, $2) +$$ LANGUAGE sql IMMUTABLE; + +CREATE OR REPLACE FUNCTION oracle.add_months(TIMESTAMP WITH TIME ZONE,INTEGER) +RETURNS TIMESTAMP +AS $$ SELECT (pg_catalog.add_months($1::pg_catalog.date, $2) + $1::time)::oracle.date; $$ +LANGUAGE SQL IMMUTABLE STRICT; + +drop view oracle.user_tables; + +create view oracle.user_tables as + select table_name + from information_schema.tables + where table_type = 'BASE TABLE'; + +-- new functionality +CREATE FUNCTION oracle.orafce_concat2(varchar2, varchar2) +RETURNS varchar2 +AS 'MODULE_PATHNAME','orafce_concat2' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION oracle.orafce_concat2(nvarchar2, nvarchar2) +RETURNS nvarchar2 +AS 'MODULE_PATHNAME','orafce_concat2' +LANGUAGE C STABLE; + +CREATE OPERATOR || (procedure = oracle.orafce_concat2, leftarg = varchar2, rightarg = varchar2); +CREATE OPERATOR || (procedure = oracle.orafce_concat2, leftarg = nvarchar2, rightarg = nvarchar2); + +CREATE OR REPLACE FUNCTION oracle.numtodsinterval(double precision, text) +RETURNS interval AS $$ + SELECT $1 * ('1' || $2)::interval +$$ LANGUAGE sql IMMUTABLE STRICT; diff --git a/src/postgres/third-party-extensions/orafce/orafce--3.7--3.8.sql b/src/postgres/third-party-extensions/orafce/orafce--3.7--3.8.sql new file mode 100644 index 000000000000..11716b9e505f --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/orafce--3.7--3.8.sql @@ -0,0 +1,21 @@ +-- bugfixes +CREATE OR REPLACE FUNCTION oracle.nvl(int, int) +RETURNS int AS $$ +SELECT coalesce($1, $2) +$$ LANGUAGE sql IMMUTABLE; + +-- this routine was renamed on PostgreSQL 12, so the wrapping should be +-- more complex and more dynamic. +CREATE OR REPLACE FUNCTION varchar2_transform(internal) +RETURNS internal +AS 'MODULE_PATHNAME','orafce_varchar_transform' +LANGUAGE C +STRICT +IMMUTABLE; + +CREATE OR REPLACE FUNCTION nvarchar2_transform(internal) +RETURNS internal +AS 'MODULE_PATHNAME','orafce_varchar_transform' +LANGUAGE C +STRICT +IMMUTABLE; diff --git a/src/postgres/third-party-extensions/orafce/orafce--3.8--3.9.sql b/src/postgres/third-party-extensions/orafce/orafce--3.8--3.9.sql new file mode 100644 index 000000000000..9ba0b71b602c --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/orafce--3.8--3.9.sql @@ -0,0 +1,13 @@ +drop view oracle.user_constraints; + +create view oracle.user_constraints as + select conname as constraint_name, + conindid::regclass as index_name, + case contype when 'p' then 'P' when 'f' then 'R' end as constraint_type, + conrelid::regclass as table_name, + case contype when 'f' then (select conname + from pg_constraint c2 + where contype = 'p' and c2.conindid = c1.conindid) + end as r_constraint_name + from pg_constraint c1, pg_class + where conrelid = pg_class.oid; diff --git a/src/postgres/third-party-extensions/orafce/orafce--3.9--3.10.sql b/src/postgres/third-party-extensions/orafce/orafce--3.9--3.10.sql new file mode 100644 index 000000000000..3ade6de4fb2c --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/orafce--3.9--3.10.sql @@ -0,0 +1 @@ +ALTER TABLE utl_file.utl_file_dir ADD COLUMN dirname text UNIQUE; diff --git a/src/postgres/third-party-extensions/orafce/orafce.c b/src/postgres/third-party-extensions/orafce/orafce.c new file mode 100644 index 000000000000..20006cea9170 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/orafce.c @@ -0,0 +1,57 @@ +#include "postgres.h" +#include "storage/lwlock.h" +#include "storage/shmem.h" +#include "utils/guc.h" +#include "commands/variable.h" + +#include "orafce.h" +#include "builtins.h" +#include "pipe.h" + +/* default value */ +char *nls_date_format = NULL; +char *orafce_timezone = NULL; + +void +_PG_init(void) +{ + +#if PG_VERSION_NUM < 90600 + + RequestAddinLWLocks(1); + +#endif + + RequestAddinShmemSpace(SHMEMMSGSZ); + + /* Define custom GUC variables. */ + DefineCustomStringVariable("orafce.nls_date_format", + "Emulate oracle's date output behaviour.", + NULL, + &nls_date_format, + NULL, + PGC_USERSET, + 0, + NULL, + NULL, NULL); + + DefineCustomStringVariable("orafce.timezone", + "Specify timezone used for sysdate function.", + NULL, + &orafce_timezone, + "GMT", + PGC_USERSET, + 0, + check_timezone, NULL, NULL); + + DefineCustomBoolVariable("orafce.varchar2_null_safe_concat", + "Specify timezone used for sysdate function.", + NULL, + &orafce_varchar2_null_safe_concat, + false, + PGC_USERSET, + 0, + NULL, NULL, NULL); + + EmitWarningsOnPlaceholders("orafce"); +} diff --git a/src/postgres/third-party-extensions/orafce/orafce.control b/src/postgres/third-party-extensions/orafce/orafce.control new file mode 100644 index 000000000000..ea567ace69a7 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/orafce.control @@ -0,0 +1,5 @@ +# orafce extension +comment = 'Functions and operators that emulate a subset of functions and packages from the Oracle RDBMS' +default_version = '3.14' +module_pathname = '$libdir/orafce' +relocatable = false diff --git a/src/postgres/third-party-extensions/orafce/orafce.h b/src/postgres/third-party-extensions/orafce/orafce.h new file mode 100644 index 000000000000..2763e3b39c00 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/orafce.h @@ -0,0 +1,68 @@ +#ifndef __ORAFCE__ +#define __ORAFCE__ + +#include "postgres.h" +#include "catalog/catversion.h" +#include "nodes/pg_list.h" +#include +#include "utils/datetime.h" +#include "utils/datum.h" + +#define TextPCopy(t) \ + DatumGetTextP(datumCopy(PointerGetDatum(t), false, -1)) + +#define PG_GETARG_IF_EXISTS(n, type, defval) \ + ((PG_NARGS() > (n) && !PG_ARGISNULL(n)) ? PG_GETARG_##type(n) : (defval)) + +/* alignment of this struct must fit for all types */ +typedef union vardata +{ + char c; + short s; + int i; + long l; + float f; + double d; + void *p; +} vardata; + +extern int ora_instr(text *txt, text *pattern, int start, int nth); +extern int ora_mb_strlen(text *str, char **sizes, int **positions); +extern int ora_mb_strlen1(text *str); + +extern char *nls_date_format; +extern char *orafce_timezone; + +extern char *nls_date_format; +extern char *orafce_timezone; + +extern bool orafce_varchar2_null_safe_concat; + +/* + * Version compatibility + */ + +extern Oid equality_oper_funcid(Oid argtype); + +/* + * Date utils + */ +#define STRING_PTR_FIELD_TYPE const char *const + +extern STRING_PTR_FIELD_TYPE ora_days[]; + +extern int ora_seq_search(const char *name, STRING_PTR_FIELD_TYPE array[], size_t max); + +#ifdef _MSC_VER + +#define int2size(v) (size_t)(v) +#define size2int(v) (int)(v) + +#else + +#define int2size(v) v +#define size2int(v) v + +#endif + +#endif diff --git a/src/postgres/third-party-extensions/orafce/others.c b/src/postgres/third-party-extensions/orafce/others.c new file mode 100644 index 000000000000..e68abefeb6b9 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/others.c @@ -0,0 +1,559 @@ +#include "postgres.h" +#include +#include +#include "catalog/pg_operator.h" +#include "catalog/pg_type.h" +#include "fmgr.h" +#include "lib/stringinfo.h" +#include "nodes/nodeFuncs.h" +#include "nodes/pg_list.h" +#include "nodes/primnodes.h" +#include "parser/parse_expr.h" +#include "parser/parse_oper.h" +#include "utils/builtins.h" +#include "utils/datum.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/syscache.h" +#include "orafce.h" +#include "builtins.h" + +/* + * Source code for nlssort is taken from postgresql-nls-string + * package by Jan Pazdziora + */ + +static char *lc_collate_cache = NULL; +static size_t multiplication = 1; + +text *def_locale = NULL; + +PG_FUNCTION_INFO_V1(ora_lnnvl); + +Datum +ora_lnnvl(PG_FUNCTION_ARGS) +{ + if (PG_ARGISNULL(0)) + PG_RETURN_BOOL(true); + + PG_RETURN_BOOL(!PG_GETARG_BOOL(0)); +} + +PG_FUNCTION_INFO_V1(ora_concat); + +Datum +ora_concat(PG_FUNCTION_ARGS) +{ + text *t1; + text *t2; + int l1; + int l2; + text *result; + + if (PG_ARGISNULL(0) && PG_ARGISNULL(1)) + PG_RETURN_NULL(); + + if (PG_ARGISNULL(0)) + PG_RETURN_DATUM(PG_GETARG_DATUM(1)); + + if (PG_ARGISNULL(1)) + PG_RETURN_DATUM(PG_GETARG_DATUM(0)); + + t1 = PG_GETARG_TEXT_PP(0); + t2 = PG_GETARG_TEXT_PP(1); + + l1 = VARSIZE_ANY_EXHDR(t1); + l2 = VARSIZE_ANY_EXHDR(t2); + + result = palloc(l1+l2+VARHDRSZ); + memcpy(VARDATA(result), VARDATA_ANY(t1), l1); + memcpy(VARDATA(result) + l1, VARDATA_ANY(t2), l2); + SET_VARSIZE(result, l1 + l2 + VARHDRSZ); + + PG_RETURN_TEXT_P(result); +} + + +PG_FUNCTION_INFO_V1(ora_nvl); + +Datum +ora_nvl(PG_FUNCTION_ARGS) +{ + if (!PG_ARGISNULL(0)) + PG_RETURN_DATUM(PG_GETARG_DATUM(0)); + + if (!PG_ARGISNULL(1)) + PG_RETURN_DATUM(PG_GETARG_DATUM(1)); + + PG_RETURN_NULL(); +} + +PG_FUNCTION_INFO_V1(ora_nvl2); + +Datum +ora_nvl2(PG_FUNCTION_ARGS) +{ + if (!PG_ARGISNULL(0)) + { + if (!PG_ARGISNULL(1)) + PG_RETURN_DATUM(PG_GETARG_DATUM(1)); + } + else + { + if (!PG_ARGISNULL(2)) + PG_RETURN_DATUM(PG_GETARG_DATUM(2)); + } + PG_RETURN_NULL(); +} + +PG_FUNCTION_INFO_V1(ora_set_nls_sort); + +Datum +ora_set_nls_sort(PG_FUNCTION_ARGS) +{ + text *arg = PG_GETARG_TEXT_P(0); + + if (def_locale != NULL) + { + pfree(def_locale); + def_locale = NULL; + } + + def_locale = (text*) MemoryContextAlloc(TopMemoryContext, VARSIZE(arg)); + memcpy(def_locale, arg, VARSIZE(arg)); + + PG_RETURN_VOID(); +} + +static text* +_nls_run_strxfrm(text *string, text *locale) +{ + char *string_str; + int string_len; + + char *locale_str = NULL; + int locale_len = 0; + + text *result; + char *tmp = NULL; + size_t size = 0; + size_t rest = 0; + int changed_locale = 0; + + /* + * Save the default, server-wide locale setting. + * It should not change during the life-span of the server so it + * is safe to save it only once, during the first invocation. + */ + if (!lc_collate_cache) + { + if ((lc_collate_cache = setlocale(LC_COLLATE, NULL))) + /* Make a copy of the locale name string. */ +#ifdef _MSC_VER + lc_collate_cache = _strdup(lc_collate_cache); +#else + lc_collate_cache = strdup(lc_collate_cache); +#endif + if (!lc_collate_cache) + elog(ERROR, "failed to retrieve the default LC_COLLATE value"); + } + + /* + * To run strxfrm, we need a zero-terminated strings. + */ + string_len = VARSIZE_ANY_EXHDR(string); + if (string_len < 0) + return NULL; + string_str = palloc(string_len + 1); + memcpy(string_str, VARDATA_ANY(string), string_len); + + *(string_str + string_len) = '\0'; + + if (locale) + { + locale_len = VARSIZE_ANY_EXHDR(locale); + } + + /* + * If different than default locale is requested, call setlocale. + */ + if (locale_len > 0 + && (strncmp(lc_collate_cache, VARDATA_ANY(locale), locale_len) + || *(lc_collate_cache + locale_len) != '\0')) + { + locale_str = palloc(locale_len + 1); + memcpy(locale_str, VARDATA_ANY(locale), locale_len); + *(locale_str + locale_len) = '\0'; + + /* + * Try to set correct locales. + * If setlocale failed, we know the default stayed the same, + * co we can safely elog. + */ + if (!setlocale(LC_COLLATE, locale_str)) + elog(ERROR, "failed to set the requested LC_COLLATE value [%s]", locale_str); + + changed_locale = 1; + } + + /* + * We do TRY / CATCH / END_TRY to catch ereport / elog that might + * happen during palloc. Ereport during palloc would not be + * nice since it would leave the server with changed locales + * setting, resulting in bad things. + */ + PG_TRY(); + { + + /* + * Text transformation. + * Increase the buffer until the strxfrm is able to fit. + */ + size = string_len * multiplication + 1; + tmp = palloc(size + VARHDRSZ); + + rest = strxfrm(tmp + VARHDRSZ, string_str, size); + while (rest >= size) + { + pfree(tmp); + size = rest + 1; + tmp = palloc(size + VARHDRSZ); + rest = strxfrm(tmp + VARHDRSZ, string_str, size); + /* + * Cache the multiplication factor so that the next + * time we start with better value. + */ + if (string_len) + multiplication = (rest / string_len) + 2; + } + } + PG_CATCH (); + { + if (changed_locale) { + /* + * Set original locale + */ + if (!setlocale(LC_COLLATE, lc_collate_cache)) + elog(FATAL, "failed to set back the default LC_COLLATE value [%s]", lc_collate_cache); + } + } + PG_END_TRY (); + + if (changed_locale) + { + /* + * Set original locale + */ + if (!setlocale(LC_COLLATE, lc_collate_cache)) + elog(FATAL, "failed to set back the default LC_COLLATE value [%s]", lc_collate_cache); + pfree(locale_str); + } + pfree(string_str); + + /* + * If the multiplication factor went down, reset it. + */ + if (string_len && rest < string_len * multiplication / 4) + multiplication = (rest / string_len) + 1; + + result = (text *) tmp; + SET_VARSIZE(result, rest + VARHDRSZ); + return result; +} + +PG_FUNCTION_INFO_V1(ora_nlssort); + +Datum +ora_nlssort(PG_FUNCTION_ARGS) +{ + text *locale; + text *result; + + if (PG_ARGISNULL(0)) + PG_RETURN_NULL(); + if (PG_ARGISNULL(1)) + { + if (def_locale != NULL) + locale = def_locale; + else + { + locale = palloc(VARHDRSZ); + SET_VARSIZE(locale, VARHDRSZ); + } + } + else + { + locale = PG_GETARG_TEXT_PP(1); + } + + result = _nls_run_strxfrm(PG_GETARG_TEXT_PP(0), locale); + + if (! result) + PG_RETURN_NULL(); + + PG_RETURN_BYTEA_P(result); +} + +PG_FUNCTION_INFO_V1(ora_decode); + +/* + * decode(lhs, [rhs, ret], ..., [default]) + */ +Datum +ora_decode(PG_FUNCTION_ARGS) +{ + int nargs; + int i; + int retarg; + + /* default value is last arg or NULL. */ + nargs = PG_NARGS(); + if (nargs % 2 == 0) + { + retarg = nargs - 1; + nargs -= 1; /* ignore the last argument */ + } + else + retarg = -1; /* NULL */ + + if (PG_ARGISNULL(0)) + { + for (i = 1; i < nargs; i += 2) + { + if (PG_ARGISNULL(i)) + { + retarg = i + 1; + break; + } + } + } + else + { + FmgrInfo *eq; + Oid collation = PG_GET_COLLATION(); + + /* + * On first call, get the input type's operator '=' and save at + * fn_extra. + */ + if (fcinfo->flinfo->fn_extra == NULL) + { + MemoryContext oldctx; + Oid typid = get_fn_expr_argtype(fcinfo->flinfo, 0); + Oid eqoid = equality_oper_funcid(typid); + + oldctx = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt); + eq = palloc(sizeof(FmgrInfo)); + fmgr_info(eqoid, eq); + MemoryContextSwitchTo(oldctx); + + fcinfo->flinfo->fn_extra = eq; + } + else + eq = fcinfo->flinfo->fn_extra; + + for (i = 1; i < nargs; i += 2) + { + Datum result; + + if (PG_ARGISNULL(i)) + continue; + + result = FunctionCall2Coll(eq, + collation, + PG_GETARG_DATUM(0), + PG_GETARG_DATUM(i)); + + if (DatumGetBool(result)) + { + retarg = i + 1; + break; + } + } + } + + if (retarg < 0 || PG_ARGISNULL(retarg)) + PG_RETURN_NULL(); + else + PG_RETURN_DATUM(PG_GETARG_DATUM(retarg)); +} + +Oid +equality_oper_funcid(Oid argtype) +{ + Oid eq; + get_sort_group_operators(argtype, false, true, false, NULL, &eq, NULL, NULL); + return get_opcode(eq); +} + +/* + * dump(anyexpr [,format]) + * + * the dump function returns a varchar2 value that includes the datatype code, + * the length in bytes, and the internal representation of the expression. + */ +PG_FUNCTION_INFO_V1(orafce_dump); + +static void +appendDatum(StringInfo str, const void *ptr, size_t length, int format) +{ + if (!PointerIsValid(ptr)) + appendStringInfoChar(str, ':'); + else + { + const unsigned char *s = (const unsigned char *) ptr; + const char *formatstr; + size_t i; + + switch (format) + { + case 8: + formatstr = "%ho"; + break; + case 10: + formatstr = "%hu"; + break; + case 16: + formatstr = "%hx"; + break; + case 17: + formatstr = "%hc"; + break; + default: + elog(ERROR, "unknown format"); + formatstr = NULL; /* quite compiler */ + } + + /* append a byte array with the specified format */ + for (i = 0; i < length; i++) + { + if (i > 0) + appendStringInfoChar(str, ','); + + /* print only ANSI visible chars */ + if (format == 17 && (iscntrl(s[i]) || !isascii(s[i]))) + appendStringInfoChar(str, '?'); + else + appendStringInfo(str, formatstr, s[i]); + } + } +} + + +Datum +orafce_dump(PG_FUNCTION_ARGS) +{ + Oid valtype = get_fn_expr_argtype(fcinfo->flinfo, 0); + int16 typlen; + bool typbyval; + Size length; + Datum value; + int format; + StringInfoData str; + + if (!OidIsValid(valtype)) + elog(ERROR, "function is called from invalid context"); + + if (PG_ARGISNULL(0)) + elog(ERROR, "argument is NULL"); + + value = PG_GETARG_DATUM(0); + format = PG_GETARG_IF_EXISTS(1, INT32, 10); + + get_typlenbyval(valtype, &typlen, &typbyval); + length = datumGetSize(value, typbyval, typlen); + + initStringInfo(&str); + appendStringInfo(&str, "Typ=%d Len=%d: ", valtype, (int) length); + + if (!typbyval) + { + appendDatum(&str, DatumGetPointer(value), length, format); + } + else if (length <= 1) + { + char v = DatumGetChar(value); + appendDatum(&str, &v, sizeof(char), format); + } + else if (length <= 2) + { + int16 v = DatumGetInt16(value); + appendDatum(&str, &v, sizeof(int16), format); + } + else if (length <= 4) + { + int32 v = DatumGetInt32(value); + appendDatum(&str, &v, sizeof(int32), format); + } + else + { + int64 v = DatumGetInt64(value); + appendDatum(&str, &v, sizeof(int64), format); + } + + PG_RETURN_TEXT_P(cstring_to_text(str.data)); +} + +PG_FUNCTION_INFO_V1(ora_get_major_version); + + +/* + * Returns current version etc, PostgreSQL 9.6, PostgreSQL 10, .. + */ +Datum +ora_get_major_version(PG_FUNCTION_ARGS) +{ + PG_RETURN_TEXT_P(cstring_to_text(PACKAGE_STRING)); +} + +PG_FUNCTION_INFO_V1(ora_get_major_version_num); + +/* + * Returns major version number 9.5, 9.6, 10, 11, .. + */ +Datum +ora_get_major_version_num(PG_FUNCTION_ARGS) +{ + PG_RETURN_TEXT_P(cstring_to_text(PG_MAJORVERSION)); +} + +PG_FUNCTION_INFO_V1(ora_get_full_version_num); + +/* + * Returns version number string - 9.5.1, 10.2, .. + */ +Datum +ora_get_full_version_num(PG_FUNCTION_ARGS) +{ + PG_RETURN_TEXT_P(cstring_to_text(PG_VERSION)); +} + +PG_FUNCTION_INFO_V1(ora_get_platform); + +/* + * 32bit, 64bit + */ +Datum +ora_get_platform(PG_FUNCTION_ARGS) +{ +#ifdef USE_FLOAT8_BYVAL + PG_RETURN_TEXT_P(cstring_to_text("64bit")); +#else + PG_RETURN_TEXT_P(cstring_to_text("32bit")); +#endif +} + +PG_FUNCTION_INFO_V1(ora_get_status); + +/* + * Production | Debug + */ +Datum +ora_get_status(PG_FUNCTION_ARGS) +{ +#ifdef USE_ASSERT_CHECKING + PG_RETURN_TEXT_P(cstring_to_text("Debug")); +#else + PG_RETURN_TEXT_P(cstring_to_text("Production")); +#endif +} diff --git a/src/postgres/third-party-extensions/orafce/parallel_schedule b/src/postgres/third-party-extensions/orafce/parallel_schedule new file mode 100644 index 000000000000..164f7361c694 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/parallel_schedule @@ -0,0 +1,3 @@ +test: init +test: dbms_pipe_session_A dbms_pipe_session_B +test: dbms_alert_session_A dbms_alert_session_B dbms_alert_session_C diff --git a/src/postgres/third-party-extensions/orafce/parse_keyword.c b/src/postgres/third-party-extensions/orafce/parse_keyword.c new file mode 100644 index 000000000000..146c27e25488 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/parse_keyword.c @@ -0,0 +1,51 @@ +#include "postgres.h" + +#include "parse_keyword.h" + +#include "parser/gramparse.h" + +#if PG_VERSION_NUM >= 90600 + +#include "common/keywords.h" + +#else + +#include "parser/keywords.h" + +#endif + +#if PG_VERSION_NUM >= 120000 + +const char * +orafce_scan_keyword(const char *text, int *keycode) +{ + int kwnum; + + kwnum = ScanKeywordLookup(text, &ScanKeywords); + if (kwnum >= 0) + { + *keycode = ScanKeywordTokens[kwnum]; + return GetScanKeyword(kwnum, &ScanKeywords); + } + + return NULL; +} + +#else + +const char * +orafce_scan_keyword(const char *text, int *keycode) +{ + const ScanKeyword *keyword; + + keyword = ScanKeywordLookup(text, ScanKeywords, NumScanKeywords); + if (keyword) + { + *keycode = keyword->value; + return keyword->name; + } + + return NULL; +} + +#endif diff --git a/src/postgres/third-party-extensions/orafce/parse_keyword.h b/src/postgres/third-party-extensions/orafce/parse_keyword.h new file mode 100644 index 000000000000..d39fd42aa4cc --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/parse_keyword.h @@ -0,0 +1,2 @@ + +extern const char *orafce_scan_keyword(const char *text, int *keycode); diff --git a/src/postgres/third-party-extensions/orafce/pipe.c b/src/postgres/third-party-extensions/orafce/pipe.c new file mode 100644 index 000000000000..a8bb3326de82 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/pipe.c @@ -0,0 +1,1319 @@ +#include "postgres.h" +#include "funcapi.h" +#include "fmgr.h" +#include "access/htup_details.h" +#include "storage/shmem.h" +#include "utils/memutils.h" +#include "utils/timestamp.h" +#include "storage/lwlock.h" +#include "miscadmin.h" +#include "string.h" +#include "lib/stringinfo.h" +#include "catalog/pg_type.h" +#include "utils/builtins.h" +#include "utils/date.h" +#include "utils/numeric.h" + +#include "shmmc.h" +#include "pipe.h" +#include "orafce.h" +#include "builtins.h" + +/* + * @ Pavel Stehule 2006-2018 + */ + +#ifndef _GetCurrentTimestamp +#define _GetCurrentTimestamp() GetCurrentTimestamp() +#endif + +#ifndef GetNowFloat +#ifdef HAVE_INT64_TIMESTAMP +#define GetNowFloat() ((float8) _GetCurrentTimestamp() / 1000000.0) +#else +#define GetNowFloat() _GetCurrentTimestamp() +#endif +#endif + +#define RESULT_DATA 0 +#define RESULT_WAIT 1 + +#define ONE_YEAR (60*60*24*365) + +PG_FUNCTION_INFO_V1(dbms_pipe_pack_message_text); +PG_FUNCTION_INFO_V1(dbms_pipe_unpack_message_text); +PG_FUNCTION_INFO_V1(dbms_pipe_send_message); +PG_FUNCTION_INFO_V1(dbms_pipe_receive_message); +PG_FUNCTION_INFO_V1(dbms_pipe_unique_session_name); +PG_FUNCTION_INFO_V1(dbms_pipe_list_pipes); +PG_FUNCTION_INFO_V1(dbms_pipe_next_item_type); +PG_FUNCTION_INFO_V1(dbms_pipe_create_pipe); +PG_FUNCTION_INFO_V1(dbms_pipe_create_pipe_2); +PG_FUNCTION_INFO_V1(dbms_pipe_create_pipe_1); +PG_FUNCTION_INFO_V1(dbms_pipe_reset_buffer); +PG_FUNCTION_INFO_V1(dbms_pipe_purge); +PG_FUNCTION_INFO_V1(dbms_pipe_remove_pipe); +PG_FUNCTION_INFO_V1(dbms_pipe_pack_message_date); +PG_FUNCTION_INFO_V1(dbms_pipe_unpack_message_date); +PG_FUNCTION_INFO_V1(dbms_pipe_pack_message_timestamp); +PG_FUNCTION_INFO_V1(dbms_pipe_unpack_message_timestamp); +PG_FUNCTION_INFO_V1(dbms_pipe_pack_message_number); +PG_FUNCTION_INFO_V1(dbms_pipe_unpack_message_number); +PG_FUNCTION_INFO_V1(dbms_pipe_pack_message_bytea); +PG_FUNCTION_INFO_V1(dbms_pipe_unpack_message_bytea); +PG_FUNCTION_INFO_V1(dbms_pipe_pack_message_record); +PG_FUNCTION_INFO_V1(dbms_pipe_unpack_message_record); +PG_FUNCTION_INFO_V1(dbms_pipe_pack_message_integer); +PG_FUNCTION_INFO_V1(dbms_pipe_pack_message_bigint); + +typedef enum { + IT_NO_MORE_ITEMS = 0, + IT_NUMBER = 9, + IT_VARCHAR = 11, + IT_DATE = 12, + IT_TIMESTAMPTZ = 13, + IT_BYTEA = 23, + IT_RECORD = 24 +} message_data_type; + +typedef struct _queue_item { + void *ptr; + struct _queue_item *next_item; +} queue_item; + +typedef struct { + bool is_valid; + bool registered; + char *pipe_name; + char *creator; + Oid uid; + struct _queue_item *items; + int16 count; + int16 limit; + int size; +} pipe; + +typedef struct { + int32 size; + message_data_type type; + Oid tupType; +} message_data_item; + +typedef struct { + int32 size; + int32 items_count; + message_data_item *next; +} message_buffer; + +#define message_buffer_size (MAXALIGN(sizeof(message_buffer))) +#define message_buffer_get_content(buf) ((message_data_item *) (((char*)buf)+message_buffer_size)) + + +#define message_data_item_size (MAXALIGN(sizeof(message_data_item))) +#define message_data_get_content(msg) (((char *)msg) + message_data_item_size) +#define message_data_item_next(msg) \ + ((message_data_item *) (message_data_get_content(msg) + MAXALIGN(msg->size))) + +typedef struct PipesFctx { + int pipe_nth; +} PipesFctx; + +typedef struct +{ +#if PG_VERSION_NUM >= 90600 + + int tranche_id; + LWLock shmem_lock; +#else + + LWLockId shmem_lockid; + +#endif + + pipe *pipes; + alert_event *events; + alert_lock *locks; + size_t size; + int sid; + vardata data[1]; /* flexible array member */ +} sh_memory; + +#define sh_memory_size (offsetof(sh_memory, data)) + +message_buffer *output_buffer = NULL; +message_buffer *input_buffer = NULL; + +pipe* pipes = NULL; + +#define NOT_INITIALIZED NULL + +LWLockId shmem_lockid = NOT_INITIALIZED;; + +int sid; /* session id */ + +extern alert_event *events; +extern alert_lock *locks; + +/* + * write on writer size bytes from ptr + */ + +static void +pack_field(message_buffer *buffer, message_data_type type, + int32 size, void *ptr, Oid tupType) +{ + int len; + message_data_item *message; + + len = MAXALIGN(size) + message_data_item_size; + if (MAXALIGN(buffer->size) + len > LOCALMSGSZ - message_buffer_size) + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"), + errdetail("Packed message is bigger than local buffer."), + errhint("Increase LOCALMSGSZ in 'pipe.h' and recompile library."))); + + if (buffer->next == NULL) + buffer->next = message_buffer_get_content(buffer); + + message = buffer->next; + + message->size = size; + message->type = type; + message->tupType = tupType; + + /* padding bytes have to be zeroed - buffer creator is responsible to clear memory */ + + memcpy(message_data_get_content(message), ptr, size); + + buffer->size += len; + buffer->items_count++; + buffer->next = message_data_item_next(message); +} + + +static void* +unpack_field(message_buffer *buffer, message_data_type *type, + int32 *size, Oid *tupType) +{ + void *ptr; + message_data_item *message; + + Assert(buffer != NULL); + Assert(buffer->items_count > 0); + Assert(buffer->next != NULL); + + message = buffer->next; + *size = message->size; + *type = message->type; + *tupType = message->tupType; + ptr = message_data_get_content(message); + + buffer->next = --buffer->items_count > 0 ? message_data_item_next(message) : NULL; + + return ptr; +} + + +/* + * Add ptr to queue. If pipe doesn't exist, register new pipe + */ + +bool +ora_lock_shmem(size_t size, int max_pipes, int max_events, int max_locks, bool reset) +{ + int i; + bool found; + + sh_memory *sh_mem; + + if (pipes == NULL) + { + sh_mem = ShmemInitStruct("dbms_pipe", size, &found); + if (sh_mem == NULL) + ereport(FATAL, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"), + errdetail("Failed while allocation block %lu bytes in shared memory.", (unsigned long) size))); + + if (!found) + { + +#if PG_VERSION_NUM >= 90600 + + sh_mem->tranche_id = LWLockNewTrancheId(); + LWLockInitialize(&sh_mem->shmem_lock, sh_mem->tranche_id); + + { + +#if PG_VERSION_NUM >= 100000 + + LWLockRegisterTranche(sh_mem->tranche_id, "orafce"); + +#else + + static LWLockTranche tranche; + + tranche.name = "orafce"; + tranche.array_base = &sh_mem->shmem_lock; + tranche.array_stride = sizeof(LWLock); + LWLockRegisterTranche(sh_mem->tranche_id, &tranche); + +#endif + + shmem_lockid = &sh_mem->shmem_lock; + } + +#else + + shmem_lockid = sh_mem->shmem_lockid = LWLockAssign(); + +#endif + + LWLockAcquire(shmem_lockid, LW_EXCLUSIVE); + + sh_mem->size = size - sh_memory_size; + ora_sinit(sh_mem->data, size, true); + pipes = sh_mem->pipes = ora_salloc(max_pipes*sizeof(pipe)); + sid = sh_mem->sid = 1; + for (i = 0; i < max_pipes; i++) + pipes[i].is_valid = false; + + events = sh_mem->events = ora_salloc(max_events*sizeof(alert_event)); + locks = sh_mem->locks = ora_salloc(max_locks*sizeof(alert_lock)); + + for (i = 0; i < max_events; i++) + { + events[i].event_name = NULL; + events[i].max_receivers = 0; + events[i].receivers = NULL; + events[i].messages = NULL; + } + for (i = 0; i < max_locks; i++) + { + locks[i].sid = -1; + locks[i].echo = NULL; + } + + } + else if (pipes == NULL) + { + +#if PG_VERSION_NUM >= 90600 + + +#if PG_VERSION_NUM >= 100000 + + LWLockRegisterTranche(sh_mem->tranche_id, "orafce"); + +#else + + static LWLockTranche tranche; + + tranche.name = "orafce"; + tranche.array_base = &sh_mem->shmem_lock; + tranche.array_stride = sizeof(LWLock); + LWLockRegisterTranche(sh_mem->tranche_id, &tranche); + +#endif + + shmem_lockid = &sh_mem->shmem_lock; + +#else + + shmem_lockid = sh_mem->shmem_lockid; + +#endif + + pipes = sh_mem->pipes; + LWLockAcquire(shmem_lockid, LW_EXCLUSIVE); + + ora_sinit(sh_mem->data, sh_mem->size, reset); + sid = ++(sh_mem->sid); + events = sh_mem->events; + locks = sh_mem->locks; + } + } + else + { + LWLockAcquire(shmem_lockid, LW_EXCLUSIVE); + } + + return pipes != NULL; +} + + +/* + * can be enhanced access/hash.h + */ + +static pipe* +find_pipe(text* pipe_name, bool* created, bool only_check) +{ + int i; + pipe *result = NULL; + + *created = false; + for (i = 0; i < MAX_PIPES; i++) + { + if (pipes[i].is_valid && + strncmp((char*)VARDATA(pipe_name), pipes[i].pipe_name, VARSIZE(pipe_name) - VARHDRSZ) == 0 + && (strlen(pipes[i].pipe_name) == (VARSIZE(pipe_name) - VARHDRSZ))) + { + /* check owner if non public pipe */ + + if (pipes[i].creator != NULL && pipes[i].uid != GetUserId()) + { + LWLockRelease(shmem_lockid); + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("insufficient privilege"), + errdetail("Insufficient privilege to access pipe"))); + } + + return &pipes[i]; + } + } + + if (only_check) + return result; + + for (i = 0; i < MAX_PIPES; i++) + if (!pipes[i].is_valid) + { + if (NULL != (pipes[i].pipe_name = ora_scstring(pipe_name))) + { + pipes[i].is_valid = true; + pipes[i].registered = false; + pipes[i].creator = NULL; + pipes[i].uid = -1; + pipes[i].count = 0; + pipes[i].limit = -1; + + *created = true; + result = &pipes[i]; + } + break; + } + + return result; +} + + +static bool +new_last(pipe *p, void *ptr) +{ + queue_item *q, *aux_q; + + if (p->count >= p->limit && p->limit != -1) + return false; + + if (p->items == NULL) + { + if (NULL == (p->items = ora_salloc(sizeof(queue_item)))) + return false; + p->items->next_item = NULL; + p->items->ptr = ptr; + p->count = 1; + return true; + } + q = p->items; + while (q->next_item != NULL) + q = q->next_item; + + + if (NULL == (aux_q = ora_salloc(sizeof(queue_item)))) + return false; + + q->next_item = aux_q; + aux_q->next_item = NULL; + aux_q->ptr = ptr; + + p->count += 1; + + return true; +} + + +static void* +remove_first(pipe *p, bool *found) +{ + struct _queue_item *q; + void *ptr = NULL; + + *found = false; + + if (NULL != (q = p->items)) + { + p->count -= 1; + ptr = q->ptr; + p->items = q->next_item; + *found = true; + + ora_sfree(q); + if (p->items == NULL && !p->registered) + { + ora_sfree(p->pipe_name); + p->is_valid = false; + } + + } + + return ptr; +} + + +/* copy message to local memory, if exists */ + +static message_buffer* +get_from_pipe(text *pipe_name, bool *found) +{ + pipe *p; + bool created; + message_buffer *shm_msg; + message_buffer *result = NULL; + + if (!ora_lock_shmem(SHMEMMSGSZ, MAX_PIPES, MAX_EVENTS, MAX_LOCKS, false)) + return NULL; + + if (NULL != (p = find_pipe(pipe_name, &created,false))) + { + if (!created) + { + if (NULL != (shm_msg = remove_first(p, found))) + { + p->size -= shm_msg->size; + + result = (message_buffer*) MemoryContextAlloc(TopMemoryContext, shm_msg->size); + memcpy(result, shm_msg, shm_msg->size); + ora_sfree(shm_msg); + } + } + } + + LWLockRelease(shmem_lockid); + + return result; +} + + +/* + * if ptr is null, then only register pipe + */ + +static bool +add_to_pipe(text *pipe_name, message_buffer *ptr, int limit, bool limit_is_valid) +{ + pipe *p; + bool created; + bool result = false; + message_buffer *sh_ptr; + + if (!ora_lock_shmem(SHMEMMSGSZ, MAX_PIPES, MAX_EVENTS, MAX_LOCKS,false)) + return false; + + for (;;) + { + if (NULL != (p = find_pipe(pipe_name, &created, false))) + { + if (created) + p->registered = ptr == NULL; + + if (limit_is_valid && (created || (p->limit < limit))) + p->limit = limit; + + if (ptr != NULL) + { + if (NULL != (sh_ptr = ora_salloc(ptr->size))) + { + memcpy(sh_ptr,ptr,ptr->size); + if (new_last(p, sh_ptr)) + { + p->size += ptr->size; + result = true; + break; + } + ora_sfree(sh_ptr); + } + if (created) + { + /* I created new pipe, but haven't memory for new value */ + ora_sfree(p->pipe_name); + p->is_valid = false; + result = false; + } + } + else + result = true; + } + break; + } + LWLockRelease(shmem_lockid); + return result; +} + + +static void +remove_pipe(text *pipe_name, bool purge) +{ + pipe *p; + bool created; + + if (NULL != (p = find_pipe(pipe_name, &created, true))) + { + queue_item *q = p->items; + while (q != NULL) + { + queue_item *aux_q; + + aux_q = q->next_item; + if (q->ptr) + ora_sfree(q->ptr); + ora_sfree(q); + q = aux_q; + } + p->items = NULL; + p->size = 0; + p->count = 0; + if (!(purge && p->registered)) + { + ora_sfree(p->pipe_name); + p->is_valid = false; + } + } +} + + +Datum +dbms_pipe_next_item_type (PG_FUNCTION_ARGS) +{ + PG_RETURN_INT32(input_buffer != NULL ? input_buffer->next->type : IT_NO_MORE_ITEMS); +} + + +static void +init_buffer(message_buffer *buffer, int32 size) +{ + memset(buffer, 0, size); + buffer->size = message_buffer_size; + buffer->items_count = 0; + buffer->next = message_buffer_get_content(buffer); +} + +static message_buffer* +check_buffer(message_buffer *buffer, int32 size) +{ + if (buffer == NULL) + { + buffer = (message_buffer*) MemoryContextAlloc(TopMemoryContext, size); + if (buffer == NULL) + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"), + errdetail("Failed while allocation block %d bytes in memory.", size))); + + init_buffer(buffer, size); + } + + return buffer; +} + +Datum +dbms_pipe_pack_message_text(PG_FUNCTION_ARGS) +{ + text *str = PG_GETARG_TEXT_PP(0); + + output_buffer = check_buffer(output_buffer, LOCALMSGSZ); + pack_field(output_buffer, IT_VARCHAR, + VARSIZE_ANY_EXHDR(str), VARDATA_ANY(str), InvalidOid); + + PG_RETURN_VOID(); +} + + +Datum +dbms_pipe_pack_message_date(PG_FUNCTION_ARGS) +{ + DateADT dt = PG_GETARG_DATEADT(0); + + output_buffer = check_buffer(output_buffer, LOCALMSGSZ); + pack_field(output_buffer, IT_DATE, + sizeof(dt), &dt, InvalidOid); + + PG_RETURN_VOID(); +} + + +Datum +dbms_pipe_pack_message_timestamp(PG_FUNCTION_ARGS) +{ + TimestampTz dt = PG_GETARG_TIMESTAMPTZ(0); + + output_buffer = check_buffer(output_buffer, LOCALMSGSZ); + pack_field(output_buffer, IT_TIMESTAMPTZ, + sizeof(dt), &dt, InvalidOid); + + PG_RETURN_VOID(); +} + + +Datum +dbms_pipe_pack_message_number(PG_FUNCTION_ARGS) +{ + Numeric num = PG_GETARG_NUMERIC(0); + + output_buffer = check_buffer(output_buffer, LOCALMSGSZ); + pack_field(output_buffer, IT_NUMBER, + VARSIZE(num) - VARHDRSZ, VARDATA(num), InvalidOid); + + PG_RETURN_VOID(); +} + + +Datum +dbms_pipe_pack_message_bytea(PG_FUNCTION_ARGS) +{ + bytea *data = PG_GETARG_BYTEA_P(0); + + output_buffer = check_buffer(output_buffer, LOCALMSGSZ); + pack_field(output_buffer, IT_BYTEA, + VARSIZE_ANY_EXHDR(data), VARDATA_ANY(data), InvalidOid); + + PG_RETURN_VOID(); +} + +static void +init_args_3(FunctionCallInfo info, Datum arg0, Datum arg1, Datum arg2) +{ + info->args[0].value = arg0; + info->args[1].value = arg1; + info->args[2].value = arg2; + info->args[0].isnull = false; + info->args[1].isnull = false; + info->args[2].isnull = false; +} + + +/* + * We can serialize only typed record + */ + +Datum +dbms_pipe_pack_message_record(PG_FUNCTION_ARGS) +{ + HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(0); + Oid tupType; + bytea *data; + + LOCAL_FCINFO(info, 3); + + tupType = HeapTupleHeaderGetTypeId(rec); + + /* + * Normally one would call record_send() using DirectFunctionCall3, + * but that does not work since record_send wants to cache some data + * using fcinfo->flinfo->fn_extra. So we need to pass it our own + * flinfo parameter. + */ + InitFunctionCallInfoData(*info, fcinfo->flinfo, 3, InvalidOid, NULL, NULL); + init_args_3(info, PointerGetDatum(rec), ObjectIdGetDatum(tupType), Int32GetDatum(-1)); + + data = (bytea*) DatumGetPointer(record_send(info)); + + output_buffer = check_buffer(output_buffer, LOCALMSGSZ); + pack_field(output_buffer, IT_RECORD, + VARSIZE(data), VARDATA(data), tupType); + + PG_RETURN_VOID(); +} + + +static Datum +dbms_pipe_unpack_message(PG_FUNCTION_ARGS, message_data_type dtype) +{ + Oid tupType; + void *ptr; + message_data_type type; + int32 size; + Datum result; + message_data_type next_type; + + if (input_buffer == NULL || + input_buffer->items_count <= 0 || + input_buffer->next == NULL || + input_buffer->next->type == IT_NO_MORE_ITEMS) + PG_RETURN_NULL(); + + next_type = input_buffer->next->type; + if (next_type != dtype) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("datatype mismatch"), + errdetail("unpack unexpected type: %d", next_type))); + + ptr = unpack_field(input_buffer, &type, &size, &tupType); + Assert(ptr != NULL); + + switch (type) + { + case IT_TIMESTAMPTZ: + result = TimestampTzGetDatum(*(TimestampTz*)ptr); + break; + case IT_DATE: + result = DateADTGetDatum(*(DateADT*)ptr); + break; + case IT_VARCHAR: + case IT_NUMBER: + case IT_BYTEA: + result = PointerGetDatum(cstring_to_text_with_len(ptr, size)); + break; + case IT_RECORD: + { + LOCAL_FCINFO(info, 3); + + StringInfoData buf; + text *data = cstring_to_text_with_len(ptr, size); + + buf.data = VARDATA(data); + buf.len = VARSIZE(data) - VARHDRSZ; + buf.maxlen = buf.len; + buf.cursor = 0; + + /* + * Normally one would call record_recv() using DirectFunctionCall3, + * but that does not work since record_recv wants to cache some data + * using fcinfo->flinfo->fn_extra. So we need to pass it our own + * flinfo parameter. + */ + InitFunctionCallInfoData(*info, fcinfo->flinfo, 3, InvalidOid, NULL, NULL); + init_args_3(info, PointerGetDatum(&buf), ObjectIdGetDatum(tupType), Int32GetDatum(-1)); + + result = record_recv(info); + break; + } + default: + elog(ERROR, "unexpected type: %d", type); + result = (Datum) 0; /* keep compiler quiet */ + } + + if (input_buffer->items_count == 0) + { + pfree(input_buffer); + input_buffer = NULL; + } + + PG_RETURN_DATUM(result); +} + + +Datum +dbms_pipe_unpack_message_text(PG_FUNCTION_ARGS) +{ + return dbms_pipe_unpack_message(fcinfo, IT_VARCHAR); +} + + +Datum +dbms_pipe_unpack_message_date(PG_FUNCTION_ARGS) +{ + return dbms_pipe_unpack_message(fcinfo, IT_DATE); +} + +Datum +dbms_pipe_unpack_message_timestamp(PG_FUNCTION_ARGS) +{ + return dbms_pipe_unpack_message(fcinfo, IT_TIMESTAMPTZ); +} + + +Datum +dbms_pipe_unpack_message_number(PG_FUNCTION_ARGS) +{ + return dbms_pipe_unpack_message(fcinfo, IT_NUMBER); +} + + +Datum +dbms_pipe_unpack_message_bytea(PG_FUNCTION_ARGS) +{ + return dbms_pipe_unpack_message(fcinfo, IT_BYTEA); +} + + +Datum +dbms_pipe_unpack_message_record(PG_FUNCTION_ARGS) +{ + return dbms_pipe_unpack_message(fcinfo, IT_RECORD); +} + + +#define WATCH_PRE(t, et, c) \ +et = GetNowFloat() + (float8)t; c = 0; \ +do \ +{ \ + +#define WATCH_POST(t,et,c) \ +if (GetNowFloat() >= et) \ +PG_RETURN_INT32(RESULT_WAIT); \ +if (cycle++ % 100 == 0) \ +CHECK_FOR_INTERRUPTS(); \ +pg_usleep(10000L); \ +} while(true && t != 0); + + +Datum +dbms_pipe_receive_message(PG_FUNCTION_ARGS) +{ + text *pipe_name = NULL; + int timeout = ONE_YEAR; + int cycle = 0; + float8 endtime; + bool found = false; + + if (PG_ARGISNULL(0)) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("pipe name is NULL"), + errdetail("Pipename may not be NULL."))); + else + pipe_name = PG_GETARG_TEXT_P(0); + + if (!PG_ARGISNULL(1)) + timeout = PG_GETARG_INT32(1); + + if (input_buffer != NULL) + { + pfree(input_buffer); + input_buffer = NULL; + } + + WATCH_PRE(timeout, endtime, cycle); + if (NULL != (input_buffer = get_from_pipe(pipe_name, &found))) + { + input_buffer->next = message_buffer_get_content(input_buffer); + break; + } +/* found empty message */ + if (found) + break; + + WATCH_POST(timeout, endtime, cycle); + PG_RETURN_INT32(RESULT_DATA); +} + + +Datum +dbms_pipe_send_message(PG_FUNCTION_ARGS) +{ + text *pipe_name = NULL; + int timeout = ONE_YEAR; + int limit = 0; + bool valid_limit; + + int cycle = 0; + float8 endtime; + + if (PG_ARGISNULL(0)) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("pipe name is NULL"), + errdetail("Pipename may not be NULL."))); + else + pipe_name = PG_GETARG_TEXT_P(0); + + output_buffer = check_buffer(output_buffer, LOCALMSGSZ); + + if (!PG_ARGISNULL(1)) + timeout = PG_GETARG_INT32(1); + + if (PG_ARGISNULL(2)) + valid_limit = false; + else + { + limit = PG_GETARG_INT32(2); + valid_limit = true; + } + + if (input_buffer != NULL) /* XXX Strange? */ + { + pfree(input_buffer); + input_buffer = NULL; + } + + WATCH_PRE(timeout, endtime, cycle); + if (add_to_pipe(pipe_name, output_buffer, + limit, valid_limit)) + break; + WATCH_POST(timeout, endtime, cycle); + + init_buffer(output_buffer, LOCALMSGSZ); + + PG_RETURN_INT32(RESULT_DATA); +} + + +Datum +dbms_pipe_unique_session_name (PG_FUNCTION_ARGS) +{ + StringInfoData strbuf; + text *result; + + float8 endtime; + int cycle = 0; + int timeout = 10; + + WATCH_PRE(timeout, endtime, cycle); + if (ora_lock_shmem(SHMEMMSGSZ, MAX_PIPES,MAX_EVENTS,MAX_LOCKS,false)) + { + initStringInfo(&strbuf); + appendStringInfo(&strbuf,"PG$PIPE$%d$%d",sid, MyProcPid); + + result = cstring_to_text_with_len(strbuf.data, strbuf.len); + pfree(strbuf.data); + LWLockRelease(shmem_lockid); + + PG_RETURN_TEXT_P(result); + } + WATCH_POST(timeout, endtime, cycle); + LOCK_ERROR(); + + PG_RETURN_NULL(); +} + +#define DB_PIPES_COLS 6 + +Datum +dbms_pipe_list_pipes(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + TupleDesc tupdesc; + AttInMetadata *attinmeta; + PipesFctx *fctx; + + float8 endtime; + int cycle = 0; + int timeout = 10; + + if (SRF_IS_FIRSTCALL()) + { + int i; + MemoryContext oldcontext; + bool has_lock = false; + + WATCH_PRE(timeout, endtime, cycle); + if (ora_lock_shmem(SHMEMMSGSZ, MAX_PIPES, MAX_EVENTS, MAX_LOCKS, false)) + { + has_lock = true; + break; + } + WATCH_POST(timeout, endtime, cycle); + if (!has_lock) + LOCK_ERROR(); + + funcctx = SRF_FIRSTCALL_INIT(); + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + fctx = palloc(sizeof(PipesFctx)); + funcctx->user_fctx = fctx; + fctx->pipe_nth = 0; + +#if PG_VERSION_NUM >= 120000 + + tupdesc = CreateTemplateTupleDesc(DB_PIPES_COLS); + +#else + + tupdesc = CreateTemplateTupleDesc(DB_PIPES_COLS, false); + +#endif + + i = 0; + TupleDescInitEntry(tupdesc, ++i, "name", VARCHAROID, -1, 0); + TupleDescInitEntry(tupdesc, ++i, "items", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, ++i, "size", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, ++i, "limit", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, ++i, "private", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, ++i, "owner", VARCHAROID, -1, 0); + Assert(i == DB_PIPES_COLS); + + attinmeta = TupleDescGetAttInMetadata(tupdesc); + funcctx->attinmeta = attinmeta; + + MemoryContextSwitchTo(oldcontext); + } + + funcctx = SRF_PERCALL_SETUP(); + fctx = (PipesFctx *) funcctx->user_fctx; + + while (fctx->pipe_nth < MAX_PIPES) + { + if (pipes[fctx->pipe_nth].is_valid) + { + Datum result; + HeapTuple tuple; + char *values[DB_PIPES_COLS]; + char items[16]; + char size[16]; + char limit[16]; + + /* name */ + values[0] = pipes[fctx->pipe_nth].pipe_name; + /* items */ + snprintf(items, lengthof(items), "%d", pipes[fctx->pipe_nth].count); + values[1] = items; + /* items */ + snprintf(size, lengthof(size), "%d", pipes[fctx->pipe_nth].size); + values[2] = size; + /* limit */ + if (pipes[fctx->pipe_nth].limit != -1) + { + snprintf(limit, lengthof(limit), "%d", pipes[fctx->pipe_nth].limit); + values[3] = limit; + } + else + values[3] = NULL; + /* private */ + values[4] = (pipes[fctx->pipe_nth].creator ? "true" : "false"); + /* owner */ + values[5] = pipes[fctx->pipe_nth].creator; + + tuple = BuildTupleFromCStrings(funcctx->attinmeta, values); + result = HeapTupleGetDatum(tuple); + + fctx->pipe_nth += 1; + SRF_RETURN_NEXT(funcctx, result); + } + fctx->pipe_nth += 1; + } + + LWLockRelease(shmem_lockid); + SRF_RETURN_DONE(funcctx); +} + +/* + * secondary functions + */ + +/* + * Registration explicit pipes + * dbms_pipe.create_pipe(pipe_name varchar, limit := -1 int, private := false bool); + */ + +Datum +dbms_pipe_create_pipe (PG_FUNCTION_ARGS) +{ + text *pipe_name = NULL; + int limit = 0; + bool is_private; + bool limit_is_valid = false; + bool created; + float8 endtime; + int cycle = 0; + int timeout = 10; + + if (PG_ARGISNULL(0)) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("pipe name is NULL"), + errdetail("Pipename may not be NULL."))); + else + pipe_name = PG_GETARG_TEXT_P(0); + + if (!PG_ARGISNULL(1)) + { + limit = PG_GETARG_INT32(1); + limit_is_valid = true; + } + + is_private = PG_ARGISNULL(2) ? false : PG_GETARG_BOOL(2); + + WATCH_PRE(timeout, endtime, cycle); + if (ora_lock_shmem(SHMEMMSGSZ, MAX_PIPES,MAX_EVENTS,MAX_LOCKS,false)) + { + pipe *p; + if (NULL != (p = find_pipe(pipe_name, &created, false))) + { + if (!created) + { + LWLockRelease(shmem_lockid); + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("pipe creation error"), + errdetail("Pipe is registered."))); + } + if (is_private) + { + char *user; + + p->uid = GetUserId(); + + user = (char*)DirectFunctionCall1(namein, + CStringGetDatum(GetUserNameFromId(p->uid, false))); + + p->creator = ora_sstrcpy(user); + pfree(user); + } + p->limit = limit_is_valid ? limit : -1; + p->registered = true; + + LWLockRelease(shmem_lockid); + PG_RETURN_VOID(); + } + } + WATCH_POST(timeout, endtime, cycle); + LOCK_ERROR(); + + PG_RETURN_VOID(); +} + + +/* + * Clean local input, output buffers + */ + +Datum +dbms_pipe_reset_buffer(PG_FUNCTION_ARGS) +{ + if (output_buffer != NULL) + { + pfree(output_buffer); + output_buffer = NULL; + } + + if (input_buffer != NULL) + { + pfree(input_buffer); + input_buffer = NULL; + } + + PG_RETURN_VOID(); +} + + +/* + * Remove all stored messages in pipe. Remove implicit created + * pipe. + */ + +Datum +dbms_pipe_purge (PG_FUNCTION_ARGS) +{ + text *pipe_name = PG_GETARG_TEXT_P(0); + + float8 endtime; + int cycle = 0; + int timeout = 10; + + WATCH_PRE(timeout, endtime, cycle); + if (ora_lock_shmem(SHMEMMSGSZ, MAX_PIPES,MAX_EVENTS,MAX_LOCKS,false)) + { + + remove_pipe(pipe_name, true); + LWLockRelease(shmem_lockid); + + PG_RETURN_VOID(); + } + WATCH_POST(timeout, endtime, cycle); + LOCK_ERROR(); + + PG_RETURN_VOID(); +} + +/* + * Remove pipe if exists + */ + +Datum +dbms_pipe_remove_pipe (PG_FUNCTION_ARGS) +{ + text *pipe_name = PG_GETARG_TEXT_P(0); + + float8 endtime; + int cycle = 0; + int timeout = 10; + + WATCH_PRE(timeout, endtime, cycle); + if (ora_lock_shmem(SHMEMMSGSZ, MAX_PIPES,MAX_EVENTS,MAX_LOCKS,false)) + { + + remove_pipe(pipe_name, false); + LWLockRelease(shmem_lockid); + + PG_RETURN_VOID(); + } + WATCH_POST(timeout, endtime, cycle); + LOCK_ERROR(); + + PG_RETURN_VOID(); +} + + +/* + * Some void udf which I can't wrap in sql + */ + +Datum +dbms_pipe_create_pipe_2 (PG_FUNCTION_ARGS) +{ + Datum arg1; + int limit = -1; + + if (PG_ARGISNULL(0)) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("pipe name is NULL"), + errdetail("Pipename may not be NULL."))); + + arg1 = PG_GETARG_DATUM(0); + + if (!PG_ARGISNULL(1)) + limit = PG_GETARG_INT32(1); + + DirectFunctionCall3(dbms_pipe_create_pipe, + arg1, + Int32GetDatum(limit), + BoolGetDatum(false)); + + PG_RETURN_VOID(); +} + +Datum +dbms_pipe_create_pipe_1 (PG_FUNCTION_ARGS) +{ + Datum arg1; + + if (PG_ARGISNULL(0)) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("pipe name is NULL"), + errdetail("Pipename may not be NULL."))); + + arg1 = PG_GETARG_DATUM(0); + + DirectFunctionCall3(dbms_pipe_create_pipe, + arg1, + (Datum) -1, + BoolGetDatum(false)); + + PG_RETURN_VOID(); +} + +Datum +dbms_pipe_pack_message_integer(PG_FUNCTION_ARGS) +{ + /* Casting from int4 to numeric */ + DirectFunctionCall1(dbms_pipe_pack_message_number, + DirectFunctionCall1(int4_numeric, PG_GETARG_DATUM(0))); + + PG_RETURN_VOID(); +} + +Datum +dbms_pipe_pack_message_bigint(PG_FUNCTION_ARGS) +{ + /* Casting from int8 to numeric */ + DirectFunctionCall1(dbms_pipe_pack_message_number, + DirectFunctionCall1(int8_numeric, PG_GETARG_DATUM(0))); + + PG_RETURN_VOID(); +} diff --git a/src/postgres/third-party-extensions/orafce/pipe.h b/src/postgres/third-party-extensions/orafce/pipe.h new file mode 100644 index 000000000000..fb2d81047f32 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/pipe.h @@ -0,0 +1,49 @@ +#ifndef __PIPE__ +#define __PIPE__ + +#define LOCALMSGSZ (8*1024) +#define SHMEMMSGSZ (30*1024) +#define MAX_PIPES 30 +#define MAX_EVENTS 30 +#define MAX_LOCKS 256 + +typedef struct _message_item { + char *message; + float8 timestamp; + struct _message_item *next_message; + struct _message_item *prev_message; + unsigned char message_id; + int *receivers; /* copy of array all registered receivers */ + int receivers_number; +} message_item; + +typedef struct _message_echo { + struct _message_item *message; + unsigned char message_id; + struct _message_echo *next_echo; +} message_echo; + +typedef struct { + char *event_name; + unsigned char max_receivers; + int *receivers; + int receivers_number; + struct _message_item *messages; +} alert_event; + +typedef struct { + int sid; + message_echo *echo; +} alert_lock; + +bool ora_lock_shmem(size_t size, int max_pipes, int max_events, int max_locks, bool reset); + +#define ERRCODE_ORA_PACKAGES_LOCK_REQUEST_ERROR MAKE_SQLSTATE('3','0', '0','0','1') + +#define LOCK_ERROR() \ + ereport(ERROR, \ + (errcode(ERRCODE_ORA_PACKAGES_LOCK_REQUEST_ERROR), \ + errmsg("lock request error"), \ + errdetail("Failed exclusive locking of shared memory."), \ + errhint("Restart PostgreSQL server."))); +#endif diff --git a/src/postgres/third-party-extensions/orafce/plunit.c b/src/postgres/third-party-extensions/orafce/plunit.c new file mode 100644 index 000000000000..e03771fb1d83 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/plunit.c @@ -0,0 +1,435 @@ +/* + * This API is subset plunit lib with http://www.apollo-pro.com/help/pl_unit_assertions.htm + * + */ + +#include "postgres.h" + +#include +#include "funcapi.h" + +#include "catalog/pg_collation.h" +#include "parser/parse_oper.h" +#include "utils/builtins.h" +#include "orafce.h" +#include "builtins.h" + +PG_FUNCTION_INFO_V1(plunit_assert_true); +PG_FUNCTION_INFO_V1(plunit_assert_true_message); +PG_FUNCTION_INFO_V1(plunit_assert_false); +PG_FUNCTION_INFO_V1(plunit_assert_false_message); +PG_FUNCTION_INFO_V1(plunit_assert_null); +PG_FUNCTION_INFO_V1(plunit_assert_null_message); +PG_FUNCTION_INFO_V1(plunit_assert_not_null); +PG_FUNCTION_INFO_V1(plunit_assert_not_null_message); +PG_FUNCTION_INFO_V1(plunit_assert_equals); +PG_FUNCTION_INFO_V1(plunit_assert_equals_message); +PG_FUNCTION_INFO_V1(plunit_assert_equals_range); +PG_FUNCTION_INFO_V1(plunit_assert_equals_range_message); +PG_FUNCTION_INFO_V1(plunit_assert_not_equals); +PG_FUNCTION_INFO_V1(plunit_assert_not_equals_message); +PG_FUNCTION_INFO_V1(plunit_assert_not_equals_range); +PG_FUNCTION_INFO_V1(plunit_assert_not_equals_range_message); +PG_FUNCTION_INFO_V1(plunit_fail); +PG_FUNCTION_INFO_V1(plunit_fail_message); + +static bool assert_equals_base(FunctionCallInfo fcinfo); +static bool assert_equals_range_base(FunctionCallInfo fcinfo); +static char *assert_get_message(FunctionCallInfo fcinfo, int nargs, char *default_message); + + +/**************************************************************** + * plunit.assert_true + * plunit.assert_true_message + * + * Syntax: + * PROCEDURE assert_true(condition boolean, message varchar default ''); + * + * Purpouse: + * Asserts that the condition is true. The optional message will be + * displayed if the assertion fails. If not supplied, a default message + * is displayed. + * + ****************************************************************/ +Datum +plunit_assert_true(PG_FUNCTION_ARGS) +{ + return plunit_assert_true_message(fcinfo); +} + +Datum +plunit_assert_true_message(PG_FUNCTION_ARGS) +{ + char *message = assert_get_message(fcinfo, 2, "plunit.assert_true exception"); + bool condition = PG_GETARG_BOOL(0); + + if (PG_ARGISNULL(0) || !condition) + ereport(ERROR, + (errcode(ERRCODE_CHECK_VIOLATION), + errmsg("%s", message), + errdetail("Plunit.assertation fails (assert_true)."))); + + PG_RETURN_VOID(); +} + +/**************************************************************** + * plunit.assert_false + * plunit.assert_false_message + * + * Syntax: + * PROCEDURE assert_false(condition boolean, message varchar default ''); + * + * Purpouse: + * Asserts that the condition is false. The optional message will be + * displayed if the assertion fails. If not supplied, a default message + * is displayed. + * + ****************************************************************/ +Datum +plunit_assert_false(PG_FUNCTION_ARGS) +{ + return plunit_assert_false_message(fcinfo); +} + +Datum +plunit_assert_false_message(PG_FUNCTION_ARGS) +{ + char *message = assert_get_message(fcinfo, 2, "plunit.assert_false exception"); + bool condition = PG_GETARG_BOOL(0); + + if (PG_ARGISNULL(0) || condition) + ereport(ERROR, + (errcode(ERRCODE_CHECK_VIOLATION), + errmsg("%s", message), + errdetail("Plunit.assertation fails (assert_false)."))); + + PG_RETURN_VOID(); +} + +/**************************************************************** + * plunit.assert_null + * plunit.assert_null_message + * + * Syntax: + * PROCEDURE assert_null(actual anyelement, message varchar default ''); + * + * Purpouse: + * Asserts that the actual is null. The optional message will be + * displayed if the assertion fails. If not supplied, a default message + * is displayed. + * + ****************************************************************/ +Datum +plunit_assert_null(PG_FUNCTION_ARGS) +{ + return plunit_assert_null_message(fcinfo); +} + +Datum +plunit_assert_null_message(PG_FUNCTION_ARGS) +{ + char *message = assert_get_message(fcinfo, 2, "plunit.assert_null exception"); + + if (!PG_ARGISNULL(0)) + ereport(ERROR, + (errcode(ERRCODE_CHECK_VIOLATION), + errmsg("%s", message), + errdetail("Plunit.assertation fails (assert_null)."))); + + PG_RETURN_VOID(); +} + +/**************************************************************** + * plunit.assert_not_null + * plunit.assert_not_null_message + * + * Syntax: + * PROCEDURE assert_not_null(actual anyelement, message varchar default ''); + * + * Purpouse: + * Asserts that the actual isn't null. The optional message will be + * displayed if the assertion fails. If not supplied, a default message + * is displayed. + * + ****************************************************************/ +Datum +plunit_assert_not_null(PG_FUNCTION_ARGS) +{ + return plunit_assert_not_null_message(fcinfo); +} + +Datum +plunit_assert_not_null_message(PG_FUNCTION_ARGS) +{ + char *message = assert_get_message(fcinfo, 2, "plunit.assert_not_null exception"); + + if (PG_ARGISNULL(0)) + ereport(ERROR, + (errcode(ERRCODE_CHECK_VIOLATION), + errmsg("%s", message), + errdetail("Plunit.assertation fails (assert_not_null)."))); + + PG_RETURN_VOID(); +} + + +/**************************************************************** + * plunit.assert_equals + * plunit.assert_equals_message + * plunit.assert_equals_range + * plunit.assert_equals_range_message + * + * Syntax: + * PROCEDURE assert_equals(expected anyelement,actual anyelement, + * message varchar default ''); + * PROCEDURE assert_equals(expected double precision, actual double precision, + * range double precision, message varchar default ''); + * + * Purpouse: + * Asserts that expected and actual are equal. The optional message will be + * displayed if the assertion fails. If not supplied, a default + * message is displayed. + * Asserts that expected and actual are within the specified range. + * The optional message will be displayed if the assertion fails. + * If not supplied, a default message is displayed. + * + ****************************************************************/ +static char * +assert_get_message(FunctionCallInfo fcinfo, int nargs, char *message) +{ + char *result; + + if (PG_NARGS() == nargs) + { + text *msg; + + if (PG_ARGISNULL(nargs - 1)) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("message is NULL"), + errdetail("Message may not be NULL."))); + + msg = PG_GETARG_TEXT_P(nargs - 1); + result = text_to_cstring(msg); + } + else + result = message; + + return result; +} + + +static bool +assert_equals_base(FunctionCallInfo fcinfo) +{ + Datum value1 = PG_GETARG_DATUM(0); + Datum value2 = PG_GETARG_DATUM(1); + Oid *ptr; + + ptr = (Oid *) fcinfo->flinfo->fn_extra; + if (ptr == NULL) + { + Oid valtype = get_fn_expr_argtype(fcinfo->flinfo, 0); + Oid eqopfcid; + + if (!OidIsValid(valtype)) + elog(ERROR, "could not determine data type of input"); + + eqopfcid = equality_oper_funcid(valtype); + + if (!OidIsValid(eqopfcid)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("unknown equal operand for datatype"))); + + /* First time calling for current query: allocate storage */ + fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, + sizeof(Oid)); + ptr = (Oid *) fcinfo->flinfo->fn_extra; + *ptr = eqopfcid; + } + + return DatumGetBool(OidFunctionCall2Coll(*ptr, DEFAULT_COLLATION_OID, value1, value2)); +} + +Datum +plunit_assert_equals(PG_FUNCTION_ARGS) +{ + return plunit_assert_equals_message(fcinfo); +} + +Datum +plunit_assert_equals_message(PG_FUNCTION_ARGS) +{ + char *message = assert_get_message(fcinfo, 3, "plunit.assert_equal exception"); + + /* skip all tests for NULL value */ + if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) + ereport(ERROR, + (errcode(ERRCODE_CHECK_VIOLATION), + errmsg("%s", message), + errdetail("Plunit.assertation fails (assert_equals)."))); + + if (!assert_equals_base(fcinfo)) + ereport(ERROR, + (errcode(ERRCODE_CHECK_VIOLATION), + errmsg("%s", message), + errdetail("Plunit.assertation fails (assert_equals)."))); + + PG_RETURN_VOID(); +} + +Datum +plunit_assert_equals_range(PG_FUNCTION_ARGS) +{ + return plunit_assert_equals_range_message(fcinfo); +} + +static bool +assert_equals_range_base(FunctionCallInfo fcinfo) +{ + float8 expected_value; + float8 actual_value; + float8 range_value; + + range_value = PG_GETARG_FLOAT8(2); + if (range_value < 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("cannot set range to negative number"))); + + expected_value = PG_GETARG_FLOAT8(0); + actual_value = PG_GETARG_FLOAT8(1); + + return fabs(expected_value - actual_value) < range_value; +} + +Datum +plunit_assert_equals_range_message(PG_FUNCTION_ARGS) +{ + char *message = assert_get_message(fcinfo, 4, "plunit.assert_equal exception"); + + /* skip all tests for NULL value */ + if (PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2)) + ereport(ERROR, + (errcode(ERRCODE_CHECK_VIOLATION), + errmsg("%s", message), + errdetail("Plunit.assertation fails (assert_equals)."))); + + if (!assert_equals_range_base(fcinfo)) + ereport(ERROR, + (errcode(ERRCODE_CHECK_VIOLATION), + errmsg("%s", message), + errdetail("Plunit.assertation fails (assert_equals)."))); + + PG_RETURN_VOID(); +} + + +/**************************************************************** + * plunit.assert_not_equals + * plunit.assert_not_equals_message + * plunit.assert_not_equals_range + * plunit.assert_not_equals_range_message + * + * Syntax: + * PROCEDURE assert_not_equals(expected anyelement,actual anyelement, + * message varchar default ''); + * PROCEDURE assert_not_equals(expected double precision, expected double precision, + * range double precision, message varchar default ''); + * + * Purpouse: + * Asserts that expected and actual are equal. The optional message will be + * displayed if the assertion fails. If not supplied, a default + * message is displayed. + * Asserts that expected and actual are within the specified range. + * The optional message will be displayed if the assertion fails. + * If not supplied, a default message is displayed. + * + ****************************************************************/ +Datum +plunit_assert_not_equals(PG_FUNCTION_ARGS) +{ + return plunit_assert_not_equals_message(fcinfo); +} + +Datum +plunit_assert_not_equals_message(PG_FUNCTION_ARGS) +{ + char *message = assert_get_message(fcinfo, 3, "plunit.assert_not_equal exception"); + + /* skip all tests for NULL value */ + if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) + ereport(ERROR, + (errcode(ERRCODE_CHECK_VIOLATION), + errmsg("%s", message), + errdetail("Plunit.assertation fails (assert_not_equals)."))); + + if (assert_equals_base(fcinfo)) + ereport(ERROR, + (errcode(ERRCODE_CHECK_VIOLATION), + errmsg("%s", message), + errdetail("Plunit.assertation fails (assert_not_equals)."))); + + PG_RETURN_VOID(); +} + +Datum +plunit_assert_not_equals_range(PG_FUNCTION_ARGS) +{ + return plunit_assert_not_equals_range_message(fcinfo); +} + +Datum +plunit_assert_not_equals_range_message(PG_FUNCTION_ARGS) +{ + char *message = assert_get_message(fcinfo, 4, "plunit.assert_not_equal exception"); + + /* skip all tests for NULL value */ + if (PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2)) + ereport(ERROR, + (errcode(ERRCODE_CHECK_VIOLATION), + errmsg("%s", message), + errdetail("Plunit.assertation fails (assert_not_equals)."))); + + if (assert_equals_range_base(fcinfo)) + ereport(ERROR, + (errcode(ERRCODE_CHECK_VIOLATION), + errmsg("%s", message), + errdetail("Plunit.assertation fails (assert_not_equals)."))); + + PG_RETURN_VOID(); +} + +/**************************************************************** + * plunit.fail + * plunit.fail_message + * + * Syntax: + * PROCEDURE fail(message varchar default ''); + * + * Purpouse: + * Fail can be used to cause a test procedure to fail + * immediately using the supplied message. + * + ****************************************************************/ + +Datum +plunit_fail(PG_FUNCTION_ARGS) +{ + return plunit_fail_message(fcinfo); +} + +Datum +plunit_fail_message(PG_FUNCTION_ARGS) +{ + char *message = assert_get_message(fcinfo, 1, "plunit.assert_fail exception"); + + ereport(ERROR, + (errcode(ERRCODE_CHECK_VIOLATION), + errmsg("%s", message), + errdetail("Plunit.assertation (assert_fail)."))); + + PG_RETURN_VOID(); +} + diff --git a/src/postgres/third-party-extensions/orafce/plvdate.c b/src/postgres/third-party-extensions/orafce/plvdate.c new file mode 100644 index 000000000000..c8b6c0e741d6 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/plvdate.c @@ -0,0 +1,921 @@ +/* + This code implements one part of functonality of + free available library PL/Vision. Please look www.quest.com + + This library isn't optimalized for big numbers, for working + with n days (n > 10000), can be slow (on my P4 31ms). + + Original author: Steven Feuerstein, 1996 - 2002 + PostgreSQL implementation author: Pavel Stehule, 2006-2018 + + This module is under BSD Licence +*/ + +#define PLVDATE_VERSION "PostgreSQL PLVdate, version 3.7, October 2018" + +#include "postgres.h" +#include "utils/date.h" +#include "utils/builtins.h" +#include +#include +#include "orafce.h" +#include "builtins.h" + +PG_FUNCTION_INFO_V1(plvdate_add_bizdays); +PG_FUNCTION_INFO_V1(plvdate_nearest_bizday); +PG_FUNCTION_INFO_V1(plvdate_next_bizday); +PG_FUNCTION_INFO_V1(plvdate_bizdays_between); +PG_FUNCTION_INFO_V1(plvdate_prev_bizday); +PG_FUNCTION_INFO_V1(plvdate_isbizday); + +PG_FUNCTION_INFO_V1(plvdate_set_nonbizday_dow); +PG_FUNCTION_INFO_V1(plvdate_unset_nonbizday_dow); +PG_FUNCTION_INFO_V1(plvdate_set_nonbizday_day); +PG_FUNCTION_INFO_V1(plvdate_unset_nonbizday_day); + +PG_FUNCTION_INFO_V1(plvdate_use_easter); +PG_FUNCTION_INFO_V1(plvdate_using_easter); +PG_FUNCTION_INFO_V1(plvdate_use_great_friday); +PG_FUNCTION_INFO_V1(plvdate_using_great_friday); +PG_FUNCTION_INFO_V1(plvdate_include_start); +PG_FUNCTION_INFO_V1(plvdate_including_start); + +PG_FUNCTION_INFO_V1(plvdate_default_holidays); + +PG_FUNCTION_INFO_V1(plvdate_version); + +PG_FUNCTION_INFO_V1(plvdate_days_inmonth); +PG_FUNCTION_INFO_V1(plvdate_isleapyear); + + +#define CHECK_SEQ_SEARCH(_l, _s) \ +do { \ + if ((_l) < 0) { \ + ereport(ERROR, \ + (errcode(ERRCODE_INVALID_DATETIME_FORMAT), \ + errmsg("invalid value for %s", (_s)))); \ + } \ +} while (0) + + +#define SUNDAY (1 << 0) +#define SATURDAY (1 << 6) + +static unsigned char nonbizdays = SUNDAY | SATURDAY; +static bool use_easter = true; +static bool use_great_friday = true; +static bool include_start = true; +static int country_id = -1; /* unknown */ + +#define MAX_holidays 30 +#define MAX_EXCEPTIONS 50 + +typedef struct { + char day; + char month; +} holiday_desc; + +typedef struct { + unsigned char nonbizdays; + bool use_easter; + bool use_great_friday; + holiday_desc *holidays; + int holidays_c; +} cultural_info; + +static holiday_desc holidays[MAX_holidays]; /* sorted array */ +static DateADT exceptions[MAX_EXCEPTIONS]; /* sorted array */ + +static int holidays_c = 0; +static int exceptions_c = 0; + +static holiday_desc czech_holidays[] = { + {1,1}, // Novy rok + {1,5}, // Svatek prace + {8,5}, // Den osvobozeni + {5,7}, // Den slovanskych verozvestu + {6,7}, // Den upaleni mistra Jana Husa + {28,9}, // Den ceske statnosti + {28,10}, // Den vzniku samostatneho ceskoslovenskeho statu + {17,11}, // Den boje za svobodu a demokracii + {24,12}, // Stedry den + {25,12}, // 1. svatek vanocni + {26,12} // 2. svatek vanocni +}; + + +static holiday_desc germany_holidays[] = { + {1,1},{1,5},{25,5},{4,6},{5,6}, + {15,8},{3,10},{25,12},{26,12} +}; + +static holiday_desc poland_holidays[] = { + {1,1},{1,5},{3,5},{15,6},{15,8}, + {1,11},{11,11},{25,12},{26,12} +}; + +static holiday_desc austria_holidays[] = { + {1,1},{6,1},{1,5},{25,5},{4,6}, + {5,6},{15,6},{15,8},{26,10},{1,11}, + {8,12},{25,12},{26,12} +}; + +static holiday_desc slovakia_holidays[] = { + {1,1},{6,1},{1,5},{8,5},{5,7}, + {29,8},{1,9},{15,9},{1,11},{17,11}, + {24,12},{25,12},{26,12} +}; + +static holiday_desc russian_holidays[] = { + {1,1},{2,1},{3,1},{4,1},{5,1}, + {7,1},{23,2},{8,3},{1,5},{9,5}, + {12,6}, {4,11} +}; + +static holiday_desc england_holidays[] = { + {1,1},{2,1},{1,5},{29,5},{28,8}, + {25,12},{26,12} +}; + +static holiday_desc usa_holidays[] = { + {1,1},{16,1},{20,2},{29,5},{4,7}, + {4,9},{9,10},{11,11},{23,11},{25,12} +}; + +cultural_info defaults_ci[] = { + {SUNDAY | SATURDAY, true, true, czech_holidays, 11}, + {SUNDAY | SATURDAY, true, true, germany_holidays, 9}, + {SUNDAY | SATURDAY, true, false, poland_holidays, 9}, + {SUNDAY | SATURDAY, true, false, austria_holidays, 13}, + {SUNDAY | SATURDAY, true, true, slovakia_holidays, 13}, + {SUNDAY | SATURDAY, false, false, russian_holidays, 12}, + {SUNDAY | SATURDAY, true, true, england_holidays, 7}, + {SUNDAY | SATURDAY, false, false, usa_holidays, 10} +}; + +STRING_PTR_FIELD_TYPE states[] = { + "Czech", "Germany", "Poland", + "Austria", "Slovakia", "Russia", + "Gb", "Usa", + NULL, +}; + +static int +dateadt_comp(const void* a, const void* b) +{ + DateADT *_a = (DateADT*)a; + DateADT *_b = (DateADT*)b; + + return *_a - *_b; +} + +static int +holiday_desc_comp(const void* a, const void* b) +{ + int result; + if (0 == (result = ((holiday_desc*)a)->month - ((holiday_desc*)b)->month)) + result = ((holiday_desc*)a)->day - ((holiday_desc*)b)->day; + + return result; +} + + +static void +calc_easter_sunday(int year, int* dd, int* mm) +{ + int b, d, e, q; + + if (year < 1900 || year > 2099) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("date is out of range"), + errdetail("Easter is defined only for years between 1900 and 2099"))); + + b = 255 - 11 * (year % 19); + d = ((b - 21) % 30) + 21; + if (d > 38) d -= 1; + e = (year + year/4 + d + 1) % 7; + q = d + 7 - e; + if (q < 32) + { + *dd = q; *mm = 3; + } + else + { + *dd = q - 31; *mm = 4; + } +} + +/* + * returns true, when day d is any easter holiday. + * + */ +static bool +easter_holidays(DateADT day, int y, int m) +{ + if (use_great_friday || use_easter) + { + if (m == 3 || m == 4) + { + int easter_sunday_day; + int easter_sunday_month; + int easter_sunday; + + calc_easter_sunday(y, &easter_sunday_day, &easter_sunday_month); + easter_sunday = date2j(y, easter_sunday_month, easter_sunday_day) - POSTGRES_EPOCH_JDATE; + + if (use_easter && (day == easter_sunday || day == easter_sunday + 1)) + return true; + + if (use_great_friday && day == easter_sunday - 2) + { + /* Great Friday is introduced in Czech Republic in 2016 */ + if (country_id == 0) + { + if (y >= 2016) + return true; + } + else + return true; + } + } + } + + return false; +} + +static DateADT +ora_add_bizdays(DateADT day, int days) +{ + int d, dx; + int y, m, auxd; + holiday_desc hd; + + d = j2day(day+POSTGRES_EPOCH_JDATE); + dx = days > 0? 1 : -1; + + while (days != 0) + { + d = (d+dx) % 7; + d = (d < 0) ? 6:d; + day += dx; + if ((1 << d) & nonbizdays) + continue; + + if (NULL != bsearch(&day, exceptions, exceptions_c, + sizeof(DateADT), dateadt_comp)) + continue; + + j2date(day + POSTGRES_EPOCH_JDATE, &y, &m, &auxd); + hd.day = (char) auxd; + hd.month = (char) m; + + if (easter_holidays(day, y, m)) + continue; + + if (NULL != bsearch(&hd, holidays, holidays_c, + sizeof(holiday_desc), holiday_desc_comp)) + continue; + + days -= dx; + } + + return day; +} + + +static int +ora_diff_bizdays(DateADT day1, DateADT day2) +{ + int d, days; + int y, m, auxd; + holiday_desc hd; + + int loops = 0; + bool start_is_bizday = false; + + DateADT aux_day; + if (day1 > day2) + { + aux_day = day1; + day1 = day2; day2 = aux_day; + } + + /* d is incremented on start of cycle, so now I have to decrease one */ + d = j2day(day1+POSTGRES_EPOCH_JDATE-1); + days = 0; + + while (day1 <= day2) + { + loops++; + day1 += 1; + d = (d+1) % 7; + + if ((1 << d) & nonbizdays) + continue; + + if (NULL != bsearch(&day1, exceptions, exceptions_c, + sizeof(DateADT), dateadt_comp)) + continue; + + j2date(day1 + POSTGRES_EPOCH_JDATE, &y, &m, &auxd); + hd.day = (char) auxd; + hd.month = (char) m; + + if (easter_holidays(day1, y, m)) + continue; + + if (NULL != bsearch(&hd, holidays, holidays_c, + sizeof(holiday_desc), holiday_desc_comp)) + continue; + + /* now the day have to be bizday, remember if first day was bizday */ + if (loops == 1) + start_is_bizday = true; + + days += 1; + } + + /* + * decrease result when first day was bizday, but we don't want + * calculate first day. + */ + if ( start_is_bizday && !include_start && days > 0) + days -= 1; + + return days; +} + + +/**************************************************************** + * PLVdate.add_bizdays + * + * Syntax: + * FUNCTION add_bizdays(IN dt DATE, IN days int) RETURNS DATE; + * + * Purpouse: + * Get the date created by adding business days to a date + * + ****************************************************************/ + + +Datum +plvdate_add_bizdays (PG_FUNCTION_ARGS) +{ + DateADT day = PG_GETARG_DATEADT(0); + int days = PG_GETARG_INT32(1); + + PG_RETURN_DATEADT(ora_add_bizdays(day,days)); +} + + +/**************************************************************** + * PLVdate.nearest_bizday + * + * Syntax: + * FUNCTION nearest_bizday(IN dt DATE) RETURNS DATE; + * + * Purpouse: + * Get the nearest business date to a given date, user defined + * + ****************************************************************/ + +Datum +plvdate_nearest_bizday (PG_FUNCTION_ARGS) +{ + DateADT dt = PG_GETARG_DATEADT(0); + DateADT d1, d2, res; + + d1 = ora_add_bizdays(dt, -1); + d2 = ora_add_bizdays(dt, 1); + + if ((dt - d1) > (d2 - dt)) + res = d2; + else + res = d1; + + PG_RETURN_DATEADT(res); +} + + +/**************************************************************** + * PLVdate.next_bizday + * + * Syntax: + * FUNCTION next_bizday(IN dt DATE) RETURNS DATE; + * + * Purpouse: + * Get the next business date from a given date, user defined + * + ****************************************************************/ + +Datum +plvdate_next_bizday (PG_FUNCTION_ARGS) +{ + DateADT day = PG_GETARG_DATEADT(0); + + PG_RETURN_DATEADT(ora_add_bizdays(day,1)); +} + + +/**************************************************************** + * PLVdate.bizdays_between + * + * Syntax: + * FUNCTION bizdays_between(IN dt1 DATE, IN dt2 DATE) + * RETURNS int; + * + * Purpouse: + * Get the number of business days between two dates + * + ****************************************************************/ + +Datum +plvdate_bizdays_between (PG_FUNCTION_ARGS) +{ + DateADT day1 = PG_GETARG_DATEADT(0); + DateADT day2 = PG_GETARG_DATEADT(1); + + PG_RETURN_INT32(ora_diff_bizdays(day1,day2)); +} + + +/**************************************************************** + * PLVdate.prev_bizday + * + * Syntax: + * FUNCTION prev_bizday(IN dt DATE) RETURNS date; + * + * Purpouse: + * Get the previous business date from a given date, user + * defined + * + ****************************************************************/ + +Datum +plvdate_prev_bizday (PG_FUNCTION_ARGS) +{ + DateADT day = PG_GETARG_DATEADT(0); + + PG_RETURN_DATEADT(ora_add_bizdays(day,-1)); +} + + +/**************************************************************** + * PLVdate.isbizday + * + * Syntax: + * FUNCTION isbizday(IN dt DATE) RETURNS bool; + * + * Purpouse: + * Call this function to determine if a date is a business day + * + ****************************************************************/ + +Datum +plvdate_isbizday (PG_FUNCTION_ARGS) +{ + DateADT day = PG_GETARG_DATEADT(0); + int y, m, d; + holiday_desc hd; + + if (0 != ((1 << j2day(day+POSTGRES_EPOCH_JDATE)) & nonbizdays)) + return false; + + if (NULL != bsearch(&day, exceptions, exceptions_c, + sizeof(DateADT), dateadt_comp)) + return false; + + j2date(day + POSTGRES_EPOCH_JDATE, &y, &m, &d); + hd.month = m; hd.day = d; + + if (easter_holidays(day, y, m)) + return false; + + PG_RETURN_BOOL (NULL == bsearch(&hd, holidays, holidays_c, + sizeof(holiday_desc), holiday_desc_comp)); +} + + +/**************************************************************** + * PLVdate.set_nonbizday + * + * Syntax: + * FUNCTION set_nonbizday(IN dow VARCHAR) RETURNS void; + * + * Purpouse: + * Set day of week as non bussines day + * + ****************************************************************/ + +Datum +plvdate_set_nonbizday_dow (PG_FUNCTION_ARGS) +{ + unsigned char check; + + text *day_txt = PG_GETARG_TEXT_PP(0); + + int d = ora_seq_search(VARDATA_ANY(day_txt), ora_days, VARSIZE_ANY_EXHDR(day_txt)); + CHECK_SEQ_SEARCH(d, "DAY/Day/day"); + + check = nonbizdays | (1 << d); + if (check == 0x7f) + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("nonbizday registeration error"), + errdetail("Constraint violation."), + errhint("One day in week have to be bizday."))); + + nonbizdays = nonbizdays | (1 << d); + + PG_RETURN_VOID(); +} + +/**************************************************************** + * PLVdate.unset_nonbizday + * + * Syntax: + * FUNCTION unset_nonbizday(IN dow VARCHAR) RETURNS void; + * + * Purpouse: + * Unset day of week as non bussines day + * + ****************************************************************/ + +Datum +plvdate_unset_nonbizday_dow (PG_FUNCTION_ARGS) +{ + text *day_txt = PG_GETARG_TEXT_PP(0); + + int d = ora_seq_search(VARDATA_ANY(day_txt), ora_days, VARSIZE_ANY_EXHDR(day_txt)); + CHECK_SEQ_SEARCH(d, "DAY/Day/day"); + + nonbizdays = (nonbizdays | (1 << d)) ^ (1 << d); + + PG_RETURN_VOID(); +} + +/**************************************************************** + * PLVdate.set_nonbizday + * + * Syntax: + * FUNCTION set_nonbizday(IN day DATE) RETURNS void; + * FUNCTION set_nonbizday(IN day DATE, IN repeat := false BOOL) RETURNS void; + * + * Purpouse: + * Set day as non bussines day, second arg specify year's + * periodicity + * + ****************************************************************/ + +Datum +plvdate_set_nonbizday_day (PG_FUNCTION_ARGS) +{ + DateADT arg1 = PG_GETARG_DATEADT(0); + bool arg2 = PG_GETARG_BOOL(1); + int y, m, d; + holiday_desc hd; + + if (arg2) + { + if (holidays_c == MAX_holidays) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("nonbizday registeration error"), + errdetail("Too much registered nonbizdays."), + errhint("Increase MAX_holidays in 'plvdate.c'."))); + + j2date(arg1 + POSTGRES_EPOCH_JDATE, &y, &m, &d); + hd.month = m; hd.day = d; + + if (NULL != bsearch(&hd, holidays, holidays_c, sizeof(holiday_desc), holiday_desc_comp)) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("nonbizday registeration error"), + errdetail("Date is registered."))); + + holidays[holidays_c].month = m; + holidays[holidays_c].day = d; + holidays_c += 1; + + qsort(holidays, holidays_c, sizeof(holiday_desc), holiday_desc_comp); + } + else + { + if (exceptions_c == MAX_EXCEPTIONS) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("nonbizday registeration error"), + errdetail("Too much registered nonrepeated nonbizdays."), + errhint("Increase MAX_EXCEPTIONS in 'plvdate.c'."))); + + if (NULL != bsearch(&arg1, exceptions, exceptions_c, sizeof(DateADT), dateadt_comp)) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("nonbizday registeration error"), + errdetail("Date is registered."))); + + exceptions[exceptions_c++] = arg1; + qsort(exceptions, exceptions_c, sizeof(DateADT), dateadt_comp); + } + + PG_RETURN_VOID(); +} + +/**************************************************************** + * PLVdate.unset_nonbizday + * + * Syntax: + * FUNCTION unset_nonbizday(IN day DATE) RETURNS void; + * FUNCTION unset_nonbizday(IN day DATE, IN repeat := false BOOL) RETURNS void; + * + * Purpouse: + * Unset day as non bussines day, second arg specify year's + * periodicity + * + ****************************************************************/ + +Datum +plvdate_unset_nonbizday_day (PG_FUNCTION_ARGS) +{ + DateADT arg1 = PG_GETARG_DATEADT(0); + bool arg2 = PG_GETARG_BOOL(1); + int y, m, d; + bool found = false; + int i; + + if (arg2) + { + j2date(arg1 + POSTGRES_EPOCH_JDATE, &y, &m, &d); + for (i = 0; i < holidays_c; i++) + { + if (!found && holidays[i].month == m && holidays[i].day == d) + found = true; + else if (found) + { + holidays[i-1].month = holidays[i].month; + holidays[i-1].day = holidays[i].day; + } + } + if (found) + holidays_c -= 1; + } + else + { + for (i = 0; i < exceptions_c; i++) + if (!found && exceptions[i] == arg1) + found = true; + else if (found) + exceptions[i-1] = exceptions[i]; + if (found) + exceptions_c -= 1; + } + if (!found) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("nonbizday unregisteration error"), + errdetail("Nonbizday not found."))); + + PG_RETURN_VOID(); +} + + +/**************************************************************** + * PLVdate.use_easter + * + * Syntax: + * FUNCTION unuse_easter() RETURNS void; + * FUNCTION use_easter() RETURNS void; + * FUNCTION use_easter(IN bool) RETURNS void + * + * Purpouse: + * Have to use easter as nonbizday? + * + ****************************************************************/ + +Datum +plvdate_use_easter (PG_FUNCTION_ARGS) +{ + use_easter = PG_GETARG_BOOL(0); + + PG_RETURN_VOID(); +} + + +/**************************************************************** + * PLVdate.using_easter + * + * Syntax: + * FUNCTION using_easter() RETURNS bool + * + * Purpouse: + * Use it easter as nonbizday? + * + ****************************************************************/ + +Datum +plvdate_using_easter (PG_FUNCTION_ARGS) +{ + PG_RETURN_BOOL(use_easter); +} + + +/**************************************************************** + * PLVdate.use_great_friday + * + * Syntax: + * FUNCTION unuse_great_friday() RETURNS void; + * FUNCTION use_great_friday() RETURNS void; + * FUNCTION use_great_friday(IN bool) RETURNS void + * + * Purpouse: + * Have to use great_friday as nonbizday? + * + ****************************************************************/ + +Datum +plvdate_use_great_friday (PG_FUNCTION_ARGS) +{ + use_great_friday = PG_GETARG_BOOL(0); + + PG_RETURN_VOID(); +} + + +/**************************************************************** + * PLVdate.using_great_friday + * + * Syntax: + * FUNCTION using_great_friday() RETURNS bool + * + * Purpouse: + * Use it great friday as nonbizday? + * + ****************************************************************/ + +Datum +plvdate_using_great_friday (PG_FUNCTION_ARGS) +{ + PG_RETURN_BOOL(use_great_friday); +} + + +/**************************************************************** + * PLVdate.include_start + * + * Syntax: + * FUNCTION include_start() RETURNS void; + * FUNCTION noinclude_start() RETURNS void; + * FUNCTION include_start(IN bool) RETURNS void + * + * Purpouse: + * Have to include current day in bizdays_between calculation? + * + ****************************************************************/ + +Datum +plvdate_include_start (PG_FUNCTION_ARGS) +{ + include_start = PG_GETARG_BOOL(0); + + PG_RETURN_VOID(); +} + + +/**************************************************************** + * PLVdate.including_start + * + * Syntax: + * FUNCTION including_start() RETURNS bool + * + * Purpouse: + * include current day in bizdays_between calculation? + * + ****************************************************************/ + +Datum +plvdate_including_start (PG_FUNCTION_ARGS) +{ + PG_RETURN_BOOL(include_start); +} + + +/* + * Load some national configurations + * + */ + +Datum +plvdate_default_holidays (PG_FUNCTION_ARGS) +{ + text *country = PG_GETARG_TEXT_PP(0); + + country_id = ora_seq_search(VARDATA_ANY(country), states, VARSIZE_ANY_EXHDR(country)); + CHECK_SEQ_SEARCH(country_id, "STATE/State/state"); + + nonbizdays = defaults_ci[country_id].nonbizdays; + use_easter = defaults_ci[country_id].use_easter; + use_great_friday = defaults_ci[country_id].use_great_friday; + exceptions_c = 0; + + holidays_c = defaults_ci[country_id].holidays_c; + memcpy(holidays, defaults_ci[country_id].holidays, holidays_c*sizeof(holiday_desc)); + + PG_RETURN_VOID(); +} + +/* + * helper maintaince functions + */ + +Datum +plvdate_version (PG_FUNCTION_ARGS) +{ + PG_RETURN_CSTRING(PLVDATE_VERSION); +} + + +/**************************************************************** + * PLVdate.days_inmonth + * + * Syntax: + * FUNCTION days_inmonth(date) RETURNS integer + * + * Purpouse: + * Returns month's length + * + ****************************************************************/ + +Datum +plvdate_days_inmonth(PG_FUNCTION_ARGS) +{ + DateADT day = PG_GETARG_DATEADT(0); + int result; + int y, m, d; + + j2date(day + POSTGRES_EPOCH_JDATE, &y, &m, &d); + + result = date2j(y, m+1, 1) - date2j(y, m, 1); + + PG_RETURN_INT32(result); +} + + +/**************************************************************** + * PLVdate.isleapyear + * + * Syntax: + * FUNCTION isleapyear() RETURNS bool + * + * Purpouse: + * Returns true, if year is leap + * + ****************************************************************/ + +Datum +plvdate_isleapyear(PG_FUNCTION_ARGS) +{ + DateADT day = PG_GETARG_DATEADT(0); + int y, m, d; + bool result; + + j2date(day + POSTGRES_EPOCH_JDATE, &y, &m, &d); + result = ((( y % 4) == 0) && ((y % 100) != 0)) || ((y / 400) == 0); + + PG_RETURN_BOOL(result); +} + +/**************************************************************** + * PLVdate.set_nonbizdays + * + * Syntax: + * FUNCTION set_nonbizdays(IN dow bool[7]) RETURNS void; + * + * Purpouse: + * Set pattern bussines/nonbussines days in week + * + ****************************************************************/ + +/**************************************************************** + * PLVdate.set_nonbizday + * + * Syntax: + * FUNCTION set_nonbizdays(IN days DATE[]) RETURNS void; + * FUNCTION set_nonbizdays(IN days DATE[], IN repeat := false BOOL) RETURNS void; + * + * Purpouse: + * Set days as non bussines day, second arg specify year's + * periodicity + * + ****************************************************************/ + +/**************************************************************** + * PLVdate.display + * + * Syntax: + * FUNCTION display() RETURNS void; + * + * Purpouse: + * Show current calendar + * + ****************************************************************/ diff --git a/src/postgres/third-party-extensions/orafce/plvlex.c b/src/postgres/third-party-extensions/orafce/plvlex.c new file mode 100644 index 000000000000..c55bfaf308f9 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/plvlex.c @@ -0,0 +1,292 @@ +/* + This code implements one part of functonality of + free available library PL/Vision. Please look www.quest.com + + Original author: Steven Feuerstein, 1996 - 2002 + PostgreSQL implementation author: Pavel Stehule, 2006-2018 + + This module is under BSD Licence + + History: + 1.0. first public version 13. March 2006 +*/ + +#include +#include + +#include "postgres.h" +#include "catalog/pg_type.h" +#include "lib/stringinfo.h" +#include "nodes/pg_list.h" +#include "utils/date.h" +#include "utils/builtins.h" +#include "plvlex.h" +#include "sqlparse.h" +#include "funcapi.h" +#include "orafce.h" +#include "builtins.h" + +typedef struct { + List *nodes; + int nnodes; + int cnode; + char **values; +} tokensFctx; + +PG_FUNCTION_INFO_V1(plvlex_tokens); + +extern int orafce_sql_yyparse(); +extern void orafce_sql_yyerror(List **result, const char *message); +extern void orafce_sql_scanner_init(const char *str); +extern void orafce_sql_scanner_finish(void); + +static orafce_lexnode *__node; + +static char *__result; +static int __len; + +#define CSTRING(txt) \ + ( \ + __len = VARSIZE(txt) - VARHDRSZ, \ + __result = palloc(__len + 1), \ + memcpy(__result, VARDATA(txt), __len), \ + __result[__len] = '\0', \ + __result) + + +#define COPY_TO_S(src,dest,col) (dest->col = (src->col ? pstrdup(src->col) : NULL)) +#define COPY_TO(src,dest,col) (dest->col = src->col) + +#define COPY_FIELDS(src,dest) \ + COPY_TO(src, dest, typenode), \ + COPY_TO_S(src,dest,str), \ + COPY_TO(src,dest,keycode), \ + COPY_TO(src,dest,lloc), \ + COPY_TO_S(src,dest,sep), \ + COPY_TO(src,dest,modificator), \ + COPY_TO(src,dest,classname) + + +#define COPY_NODE(src) \ + ( \ + __node = (orafce_lexnode*) palloc(sizeof(orafce_lexnode)), \ + COPY_FIELDS(src,__node), \ + __node) + + +/* Finding triplet a.b --> a */ + +#define IsType(node, type) (node->typenode == X_##type) +#define APPEND_NODE(list,nd) \ + if (nd) \ + { \ + list = lappend(list, nd); \ + nd = NULL; \ + } + +#define mod(a) (a->modificator) +#define SF(a) (a ? a : "") + +#define NEWNODE(type) \ + ( \ + __node = (orafce_lexnode *) palloc(sizeof(orafce_lexnode)), \ + __node->typenode = X_##type, \ + __node->modificator = NULL, \ + __node->sep = NULL, \ + __node->keycode = -1, \ + __node->classname = #type, \ + __node->lloc = 0, \ + __node ) + + + +static orafce_lexnode * +compose(orafce_lexnode *a, orafce_lexnode *b) +{ + orafce_lexnode *result; + StringInfo sinfo; + + sinfo = makeStringInfo(); + result = NEWNODE(IDENT); + result->lloc = a->lloc; + + if (strcmp(SF(mod(a)), "dq") == 0) + appendStringInfo(sinfo, "\"%s\".", a->str); + else + { + appendStringInfoString(sinfo, a->str); + appendStringInfoChar(sinfo, '.'); + } + + if (strcmp(SF(mod(b)), "dq") == 0) + appendStringInfo(sinfo, "\"%s\"", b->str); + else + appendStringInfoString(sinfo, b->str); + + result->str = sinfo->data; + + return result; +} + +static List * +filterList(List *list, bool skip_spaces, bool qnames) +{ + List *result = NIL; + ListCell *cell; + bool isdot = false; + orafce_lexnode *a = NULL; + orafce_lexnode *dot = NULL; + + foreach(cell, list) + { + orafce_lexnode *nd = (orafce_lexnode *) lfirst(cell); + + if (qnames) + { + isdot = (IsType(nd, OTHERS) && (nd->str[0] == '.')); + + if (IsType(nd, IDENT) && dot && a) + { + a = compose(a, nd); + dot = NULL; + continue; + } + else if (isdot && !dot && a) + { + dot = COPY_NODE(nd); + continue; + } + else if (IsType(nd, IDENT) && !a) + { + a = COPY_NODE(nd); + continue; + } + } + + /* clean buffered values */ + APPEND_NODE(result,a); + APPEND_NODE(result,dot); + + if (!(skip_spaces && IsType(nd, WHITESPACE))) + { + result = lappend(result, COPY_NODE(nd)); + } + } + + /* clean buffered values */ + APPEND_NODE(result,a); + APPEND_NODE(result,dot); + + return result; +} + +Datum +plvlex_tokens(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + TupleDesc tupdesc; + AttInMetadata *attinmeta; + tokensFctx *fctx; + + + if (SRF_IS_FIRSTCALL ()) + { + MemoryContext oldcontext; + List *lexems; + text *src = PG_GETARG_TEXT_P(0); + bool skip_spaces = PG_GETARG_BOOL(1); + bool qnames = PG_GETARG_BOOL(2); + + orafce_sql_scanner_init(CSTRING(src)); + if (orafce_sql_yyparse(&lexems) != 0) + orafce_sql_yyerror(NULL, "bogus input"); + + orafce_sql_scanner_finish(); + + funcctx = SRF_FIRSTCALL_INIT (); + oldcontext = MemoryContextSwitchTo (funcctx->multi_call_memory_ctx); + + fctx = (tokensFctx*) palloc (sizeof (tokensFctx)); + funcctx->user_fctx = (void *)fctx; + + fctx->nodes = filterList(lexems, skip_spaces, qnames); + fctx->nnodes = list_length(fctx->nodes); + fctx->cnode = 0; + + fctx->values = (char **) palloc (6 * sizeof (char *)); + fctx->values [0] = (char*) palloc (16 * sizeof (char)); + fctx->values [1] = (char*) palloc (1024 * sizeof (char)); + fctx->values [2] = (char*) palloc (16 * sizeof (char)); + fctx->values [3] = (char*) palloc (16 * sizeof (char)); + fctx->values [4] = (char*) palloc (255 * sizeof (char)); + fctx->values [5] = (char*) palloc (255 * sizeof (char)); + +#if PG_VERSION_NUM >= 120000 + + tupdesc = CreateTemplateTupleDesc (6); + +#else + + tupdesc = CreateTemplateTupleDesc (6, false); + +#endif + + TupleDescInitEntry (tupdesc, 1, "start_pos", INT4OID, -1, 0); + TupleDescInitEntry (tupdesc, 2, "token", TEXTOID, -1, 0); + TupleDescInitEntry (tupdesc, 3, "keycode", INT4OID, -1, 0); + TupleDescInitEntry (tupdesc, 4, "class", TEXTOID, -1, 0); + TupleDescInitEntry (tupdesc, 5, "separator", TEXTOID, -1, 0); + TupleDescInitEntry (tupdesc, 6, "mod", TEXTOID, -1, 0); + + attinmeta = TupleDescGetAttInMetadata (tupdesc); + funcctx -> attinmeta = attinmeta; + + MemoryContextSwitchTo (oldcontext); + } + + + funcctx = SRF_PERCALL_SETUP (); + fctx = (tokensFctx*) funcctx->user_fctx; + + while (fctx->cnode < fctx->nnodes) + { + char **values; + Datum result; + HeapTuple tuple; + char *back_vals[6]; + + orafce_lexnode *nd = (orafce_lexnode*) list_nth(fctx->nodes, fctx->cnode++); + values = fctx->values; + + back_vals[2] = values[2]; + back_vals[4] = values[4]; + back_vals[5] = values[5]; + + snprintf(values[0], 16, "%d", nd->lloc); + snprintf(values[1], 10000, "%s", SF(nd->str)); + snprintf(values[2], 16, "%d", nd->keycode); + snprintf(values[3], 16, "%s", nd->classname); + snprintf(values[4], 255, "%s", SF(nd->sep)); + snprintf(values[5], 48, "%s", SF(nd->modificator)); + + if (nd->keycode == -1) + values[2] = NULL; + + if (!nd->sep) + values[4] = NULL; + + if (!nd->modificator) + values[5] = NULL; + + tuple = BuildTupleFromCStrings(funcctx->attinmeta, fctx->values); + result = HeapTupleGetDatum(tuple); + + values[2] = back_vals[2]; + values[4] = back_vals[4]; + values[5] = back_vals[5]; + + SRF_RETURN_NEXT (funcctx, result); + } + + SRF_RETURN_DONE (funcctx); +} diff --git a/src/postgres/third-party-extensions/orafce/plvlex.h b/src/postgres/third-party-extensions/orafce/plvlex.h new file mode 100644 index 000000000000..f56402355311 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/plvlex.h @@ -0,0 +1,10 @@ +typedef struct +{ + int typenode; + char *str; + int keycode; + int lloc; + char *sep; + char *modificator; + char *classname; +} orafce_lexnode; diff --git a/src/postgres/third-party-extensions/orafce/plvstr.c b/src/postgres/third-party-extensions/orafce/plvstr.c new file mode 100644 index 000000000000..74ded5d5a772 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/plvstr.c @@ -0,0 +1,1344 @@ +/* + This code implements one part of functonality of + free available library PL/Vision. Please look www.quest.com + + Original author: Steven Feuerstein, 1996 - 2002 + PostgreSQL implementation author: Pavel Stehule, 2006-2018 + + This module is under BSD Licence + + History: + 1.0. first public version 13. March 2006 +*/ + + +#include "postgres.h" +#include "utils/builtins.h" +#include "utils/numeric.h" +#include "string.h" +#include "stdlib.h" +#include "utils/pg_locale.h" +#include "mb/pg_wchar.h" +#include "nodes/execnodes.h" + +#include "catalog/pg_type.h" +#include "libpq/pqformat.h" +#include "orafce.h" +#include "builtins.h" + +PG_FUNCTION_INFO_V1(plvstr_rvrs); +PG_FUNCTION_INFO_V1(plvstr_normalize); +PG_FUNCTION_INFO_V1(plvstr_is_prefix_text); +PG_FUNCTION_INFO_V1(plvstr_is_prefix_int); +PG_FUNCTION_INFO_V1(plvstr_is_prefix_int64); +PG_FUNCTION_INFO_V1(plvstr_lpart); +PG_FUNCTION_INFO_V1(plvstr_rpart); +PG_FUNCTION_INFO_V1(plvstr_lstrip); +PG_FUNCTION_INFO_V1(plvstr_rstrip); +PG_FUNCTION_INFO_V1(plvstr_left); +PG_FUNCTION_INFO_V1(plvstr_right); +PG_FUNCTION_INFO_V1(plvstr_substr2); +PG_FUNCTION_INFO_V1(plvstr_substr3); +PG_FUNCTION_INFO_V1(plvstr_instr2); +PG_FUNCTION_INFO_V1(plvstr_instr3); +PG_FUNCTION_INFO_V1(plvstr_instr4); +PG_FUNCTION_INFO_V1(plvstr_betwn_i); +PG_FUNCTION_INFO_V1(plvstr_betwn_c); +PG_FUNCTION_INFO_V1(plvstr_swap); + +PG_FUNCTION_INFO_V1(plvchr_nth); +PG_FUNCTION_INFO_V1(plvchr_first); +PG_FUNCTION_INFO_V1(plvchr_last); +PG_FUNCTION_INFO_V1(plvchr_is_kind_i); +PG_FUNCTION_INFO_V1(plvchr_is_kind_a); +PG_FUNCTION_INFO_V1(plvchr_char_name); + +PG_FUNCTION_INFO_V1(oracle_substr2); +PG_FUNCTION_INFO_V1(oracle_substr3); + +static text *ora_substr(Datum str, int start, int len); + +#define ora_substr_text(str, start, len) \ + ora_substr(PointerGetDatum((str)), (start), (len)) + +static const char* char_names[] = { + "NULL","SOH","STX","ETX","EOT","ENQ","ACK","DEL", + "BS", "HT", "NL", "VT", "NP", "CR", "SO", "SI", + "DLE", "DC1","DC2","DC3","DC4","NAK","SYN","ETB", + "CAN", "EM","SUB","ESC","FS","GS","RS","US","SP" +}; + +#define NON_EMPTY_CHECK(str) \ +if (VARSIZE_ANY_EXHDR(str) == 0) \ + ereport(ERROR, \ + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), \ + errmsg("invalid parameter"), \ + errdetail("Not allowed empty string."))); + +#define PARAMETER_ERROR(detail) \ + ereport(ERROR, \ + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), \ + errmsg("invalid parameter"), \ + errdetail(detail))); + + +#ifndef _pg_mblen +#define _pg_mblen pg_mblen +#endif + +typedef enum +{ + POSITION, + FIRST, + LAST +} position_mode; + +/* + * Make substring, can handle negative start + * + */ +int +ora_mb_strlen(text *str, char **sizes, int **positions) +{ + int r_len; + int cur_size = 0; + int sz; + char *p; + int cur = 0; + + p = VARDATA_ANY(str); + r_len = VARSIZE_ANY_EXHDR(str); + + if (NULL != sizes) + *sizes = palloc(r_len * sizeof(char)); + if (NULL != positions) + *positions = palloc(r_len * sizeof(int)); + + while (cur < r_len) + { + sz = _pg_mblen(p); + if (sizes) + (*sizes)[cur_size] = sz; + if (positions) + (*positions)[cur_size] = cur; + cur += sz; + p += sz; + cur_size += 1; + } + + return cur_size; +} + + +int +ora_mb_strlen1(text *str) +{ + int r_len; + int c; + char *p; + + r_len = VARSIZE_ANY_EXHDR(str); + + if (pg_database_encoding_max_length() == 1) + return r_len; + + p = VARDATA_ANY(str); + c = 0; + while (r_len > 0) + { + int sz; + + sz = _pg_mblen(p); + p += sz; + r_len -= sz; + c += 1; + } + + return c; +} + +/* + * len < 0 means "length is not specified". + */ +static text * +ora_substr(Datum str, int start, int len) +{ + if (start == 0) + start = 1; /* 0 is interpreted as 1 */ + else if (start < 0) + { + text *t; + int32 n; + + t = DatumGetTextPP(str); + n = pg_mbstrlen_with_len(VARDATA_ANY(t), VARSIZE_ANY_EXHDR(t)); + start = n + start + 1; + if (start <= 0) + return cstring_to_text(""); + str = PointerGetDatum(t); /* save detoasted text */ + } + + if (len < 0) + return DatumGetTextP(DirectFunctionCall2(text_substr_no_len, + str, Int32GetDatum(start))); + else + return DatumGetTextP(DirectFunctionCall3(text_substr, + str, Int32GetDatum(start), Int32GetDatum(len))); +} + +/* simply search algorhitm - can be better */ + +static int +ora_instr_mb(text *txt, text *pattern, int start, int nth) +{ + int c_len_txt, c_len_pat; + int b_len_pat; + int *pos_txt; + const char *str_txt, *str_pat; + int beg, end, i, dx; + + str_txt = VARDATA_ANY(txt); + c_len_txt = ora_mb_strlen(txt, NULL, &pos_txt); + str_pat = VARDATA_ANY(pattern); + b_len_pat = VARSIZE_ANY_EXHDR(pattern); + c_len_pat = pg_mbstrlen_with_len(str_pat, b_len_pat); + + if (start > 0) + { + dx = 1; + beg = start - 1; + end = c_len_txt - c_len_pat + 1; + if (beg >= end) + return 0; /* out of range */ + } + else + { + dx = -1; + beg = Min(c_len_txt + start, c_len_txt - c_len_pat); + end = -1; + if (beg <= end) + return 0; /* out of range */ + } + + for (i = beg; i != end; i += dx) + { + if (memcmp(str_txt + pos_txt[i], str_pat, b_len_pat) == 0) + { + if (--nth == 0) + return i + 1; + } + } + + return 0; +} + + +int +ora_instr(text *txt, text *pattern, int start, int nth) +{ + int len_txt, len_pat; + const char *str_txt, *str_pat; + int beg, end, i, dx; + + if (nth <= 0) + PARAMETER_ERROR("Four parameter isn't positive."); + + /* Forward for multibyte strings */ + if (pg_database_encoding_max_length() > 1) + return ora_instr_mb(txt, pattern, start, nth); + + str_txt = VARDATA_ANY(txt); + len_txt = VARSIZE_ANY_EXHDR(txt); + str_pat = VARDATA_ANY(pattern); + len_pat = VARSIZE_ANY_EXHDR(pattern); + + if (start > 0) + { + dx = 1; + beg = start - 1; + end = len_txt - len_pat + 1; + if (beg >= end) + return 0; /* out of range */ + } + else + { + dx = -1; + beg = Min(len_txt + start, len_txt - len_pat); + end = -1; + if (beg <= end) + return 0; /* out of range */ + } + + for (i = beg; i != end; i += dx) + { + if (memcmp(str_txt + i, str_pat, len_pat) == 0) + { + if (--nth == 0) + return i + 1; + } + } + + return 0; +} + + +/**************************************************************** + * PLVstr.normalize + * + * Syntax: + * FUNCTION plvstr.normalize (string_in IN VARCHAR) + * RETURN VARCHAR; + * + * Purpouse: + * Normalize string - replace white chars by space, replace + * spaces by space + * + ****************************************************************/ + +Datum +plvstr_normalize(PG_FUNCTION_ARGS) +{ + text *str = PG_GETARG_TEXT_PP(0); + text *result; + char *aux, *aux_cur; + int i; + +#if defined(_MSC_VER) && (defined(_M_X64) || defined(__amd64__)) + + __int64 l; + +#else + + int l; + +#endif + + char c, *cur; + bool write_spc = false; + bool ignore_stsp = true; + bool mb_encode; + int sz; + + mb_encode = pg_database_encoding_max_length() > 1; + + l = VARSIZE_ANY_EXHDR(str); + aux_cur = aux = palloc(l); + + write_spc = false; + cur = VARDATA_ANY(str); + + for (i = 0; i < l; i++) + { + switch ((c = *cur)) + { + case '\t': + case '\n': + case '\r': + case ' ': + write_spc = ignore_stsp ? false : true; + break; + default: + /* ignore all other unvisible chars */ + + if (mb_encode) + { + sz = _pg_mblen(cur); + if (sz > 1 || (sz == 1 && c > 32)) + { + int j; + + if (write_spc) + { + *aux_cur++ = ' '; + write_spc = false; + + } + for (j = 0; j < sz; j++) + { + *aux_cur++ = *cur++; + } + ignore_stsp = false; + i += sz - 1; + } + continue; + + } + else + if (c > 32) + { + if (write_spc) + { + *aux_cur++ = ' '; + write_spc = false; + } + *aux_cur++ = c; + ignore_stsp = false; + continue; + } + + } + cur += 1; + } + + l = aux_cur - aux; + result = palloc(l+VARHDRSZ); + SET_VARSIZE(result, l + VARHDRSZ); + memcpy(VARDATA(result), aux, l); + + PG_RETURN_TEXT_P(result); +} + + +/**************************************************************** + * PLVstr.instr + * + * Syntax: + * FUNCTION plvstr.instr (string_in VARCHAR, pattern VARCHAR) + * FUNCTION plvstr.instr (string_in VARCHAR, pattern VARCHAR, + * start_in INTEGER) + * FUNCTION plvstr.instr (string_in VARCHAR, pattern VARCHAR, + * start_in INTEGER, nth INTEGER) + * RETURN INT; + * + * Purpouse: + * Search pattern in string. + * + ****************************************************************/ + +Datum +plvstr_instr2 (PG_FUNCTION_ARGS) +{ + text *arg1 = PG_GETARG_TEXT_PP(0); + text *arg2 = PG_GETARG_TEXT_PP(1); + + PG_RETURN_INT32(ora_instr(arg1, arg2, 1, 1)); +} + +Datum +plvstr_instr3 (PG_FUNCTION_ARGS) +{ + text *arg1 = PG_GETARG_TEXT_PP(0); + text *arg2 = PG_GETARG_TEXT_PP(1); + int arg3 = PG_GETARG_INT32(2); + + PG_RETURN_INT32(ora_instr(arg1, arg2, arg3, 1)); +} + +Datum +plvstr_instr4 (PG_FUNCTION_ARGS) +{ + text *arg1 = PG_GETARG_TEXT_PP(0); + text *arg2 = PG_GETARG_TEXT_PP(1); + int arg3 = PG_GETARG_INT32(2); + int arg4 = PG_GETARG_INT32(3); + + PG_RETURN_INT32(ora_instr(arg1, arg2, arg3, arg4)); +} + + +/**************************************************************** + * PLVstr.is_prefix + * + * Syntax: + * FUNCTION plvstr.is_prefix (string_in IN VARCHAR, + * prefix_in VARCHAR, + * case_sensitive BOOL := true) + * RETURN bool; + * FUNCTION plvstr.is_prefix (num_in IN NUMERIC, + * prefix_in NUMERIC) RETURN bool; + * FUNCTION plvstr.is_prefix (int_in IN INT, + * prefix_in INT) RETURN bool; + * + * Purpouse: + * Returns true, if prefix_in is prefix of string_in + * + ****************************************************************/ + + +Datum +plvstr_is_prefix_text (PG_FUNCTION_ARGS) +{ + text *str = PG_GETARG_TEXT_PP(0); + text *prefix = PG_GETARG_TEXT_PP(1); + bool case_sens = PG_GETARG_BOOL(2); + bool mb_encode; + + int str_len = VARSIZE_ANY_EXHDR(str); + int pref_len = VARSIZE_ANY_EXHDR(prefix); + + int i; + char *ap, *bp; + + + mb_encode = pg_database_encoding_max_length() > 1; + + if (mb_encode && !case_sens) + { + str = (text*)DatumGetPointer(DirectFunctionCall1(lower, PointerGetDatum(str))); + prefix = (text*)DatumGetPointer(DirectFunctionCall1(lower, PointerGetDatum(prefix))); + } + + ap = VARDATA_ANY(str); + bp = VARDATA_ANY(prefix); + + for (i = 0; i < pref_len; i++) + { + if (i >= str_len) + break; + if (case_sens || mb_encode) + { + if (*ap++ != *bp++) + break; + } + else if (!mb_encode) + { + if (pg_toupper((unsigned char) *ap++) != pg_toupper((unsigned char) *bp++)) + break; + } + } + + PG_RETURN_BOOL(i == pref_len); +} + +Datum +plvstr_is_prefix_int (PG_FUNCTION_ARGS) +{ + int n = PG_GETARG_INT32(0); + int prefix = PG_GETARG_INT32(1); + bool result = false; + + do + { + if (n == prefix) + { + result = true; + break; + } + n = n / 10; + + } while (n >= prefix); + + PG_RETURN_BOOL(result); +} + +Datum +plvstr_is_prefix_int64 (PG_FUNCTION_ARGS) +{ + int64 n = PG_GETARG_INT64(0); + int64 prefix = PG_GETARG_INT64(1); + bool result = false; + + do + { + if (n == prefix) + { + result = true; + break; + } + n = n / 10; + + } while (n >= prefix); + + PG_RETURN_BOOL(result); +} + + +/**************************************************************** + * PLVstr.rvrs + * + * Syntax: + * FUNCTION plvstr.rvrs (string_in IN VARCHAR, + * start_in IN INTEGER := 1, + * end_in IN INTEGER := NULL) + * RETURN VARCHAR2; + * + * Purpouse: + * Reverse string or part of string + * + ****************************************************************/ + +Datum +plvstr_rvrs(PG_FUNCTION_ARGS) +{ + text *str; + int start; + int end; + int len; + int i; + int new_len; + text *result; + char *data; + char *sizes = NULL; + int *positions = NULL; + bool mb_encode; + + if (PG_ARGISNULL(0)) + PG_RETURN_NULL(); + + str = PG_GETARG_TEXT_PP(0); + + mb_encode = pg_database_encoding_max_length() > 1; + + if (!mb_encode) + len = VARSIZE_ANY_EXHDR(str); + else + len = ora_mb_strlen(str, &sizes, &positions); + + start = PG_ARGISNULL(1) ? 1 : PG_GETARG_INT32(1); + end = PG_ARGISNULL(2) ? (start < 0 ? -len : len) : PG_GETARG_INT32(2); + + if ((start > end && start > 0) || (start < end && start < 0)) + PARAMETER_ERROR("Second parameter is bigger than third."); + + if (start < 0) + { + int new_start, new_end; + + new_start = len + start + 1; + new_end = len + end + 1; + start = new_end; + end = new_start; + } + + start = start != 0 ? start : 1; + end = end < len ? end : len; + + new_len = end - start + 1; + new_len = new_len >= 0 ? new_len : 0; + + if (mb_encode) + { + int max_size; + int cur_size; + char *p; + int j; + int fz_size; + + fz_size = VARSIZE_ANY_EXHDR(str); + + if ((max_size = (new_len*pg_database_encoding_max_length())) > fz_size) + result = palloc(fz_size + VARHDRSZ); + else + result = palloc(max_size + VARHDRSZ); + data = (char*) VARDATA(result); + + cur_size = 0; + p = VARDATA_ANY(str); + for (i = end - 1; i>= start - 1; i--) + { + for (j=0; j= start - 1; i--) + *data++ = p[i]; + } + + PG_RETURN_TEXT_P(result); +} + + +/**************************************************************** + * PLVstr.lpart + * + * Syntax: + * FUNCTION PLVstr.lpart (string_in IN VARCHAR, + * divider_in IN VARCHAR, + * start_in IN INTEGER := 1, + * nth_in IN INTEGER := 1, + * all_if_notfound_in IN BOOLEAN := FALSE) + * RETURN VARCHAR2; + * + * Purpouse: + * Call this function to return the left part of a string. + * + ****************************************************************/ + +Datum +plvstr_lpart (PG_FUNCTION_ARGS) +{ + text *str = PG_GETARG_TEXT_P(0); + text *div = PG_GETARG_TEXT_P(1); + int start = PG_GETARG_INT32(2); + int nth = PG_GETARG_INT32(3); + bool all_if_notfound = PG_GETARG_BOOL(4); + int loc; + + loc = ora_instr(str, div, start, nth); + if (loc == 0) + { + if (all_if_notfound) + PG_RETURN_TEXT_P(TextPCopy(str)); + else + PG_RETURN_NULL(); + } + else + PG_RETURN_TEXT_P(ora_substr_text(str, 1, loc-1)); +} + + +/**************************************************************** + * PLVstr.rpart + * + * Syntax: + * FUNCTION PLVstr.rpart (string_in IN VARCHAR, + * divider_in IN VARCHAR, + * start_in IN INTEGER := 1, + * nth_in IN INTEGER := 1, + * all_if_notfound_in IN BOOLEAN := FALSE) + * RETURN VARCHAR2; + * + * Purpouse: + * Call this function to return the right part of a string. + * + ****************************************************************/ + +Datum +plvstr_rpart (PG_FUNCTION_ARGS) +{ + text *str = PG_GETARG_TEXT_P(0); + text *div = PG_GETARG_TEXT_P(1); + int start = PG_GETARG_INT32(2); + int nth = PG_GETARG_INT32(3); + bool all_if_notfound = PG_GETARG_BOOL(4); + int loc; + + loc = ora_instr(str, div, start, nth); + if (loc == 0) + { + if (all_if_notfound) + PG_RETURN_TEXT_P(TextPCopy(str)); + else + PG_RETURN_NULL(); + } + else + PG_RETURN_TEXT_P(ora_substr_text(str, loc+1, -1)); +} + + +/**************************************************************** + * PLVstr.lstrip + * + * Syntax: + * FUNCTION plvstr.lstrip (string_in IN VARCHAR, + * substring_in IN VARCHAR, + * num_in IN INTEGER := 1) + * RETURN VARCHAR; + * + * Purpouse: + * Call this function to remove characters from the beginning + * (left) of a string. + * + ****************************************************************/ + +Datum +plvstr_lstrip (PG_FUNCTION_ARGS) +{ + text *str = PG_GETARG_TEXT_PP(0); + text *pat = PG_GETARG_TEXT_PP(1); + int num = PG_GETARG_INT32(2); + int count = 0; + int len_p, len_s, i; + + char *str_p, *aux_str_p, *pat_p; + len_p = VARSIZE_ANY_EXHDR(pat); + len_s = VARSIZE_ANY_EXHDR(str); + + str_p = VARDATA_ANY(str); + while (count < num) + { + pat_p = VARDATA_ANY(pat); + aux_str_p = str_p; + + if (len_s < len_p) + break; + + for (i = 0; i < len_p; i++) + if (*aux_str_p++ != *pat_p++) + break; + + if (i >= len_p) + { + count++; + /* found */ + str_p = aux_str_p; + len_s -= len_p; + continue; + } + break; + } + + PG_RETURN_TEXT_P(cstring_to_text_with_len(str_p,len_s)); +} + + +/**************************************************************** + * PLVstr.rstrip + * + * Syntax: + * FUNCTION plvstr.rstrip (string_in IN VARCHAR, + * substring_in IN VARCHAR, + * num_in IN INTEGER := 1) + * RETURN VARCHAR; + * + * Purpouse: + * Call this function to remove characters from the end + * (right) of a string. + * + ****************************************************************/ + +Datum +plvstr_rstrip (PG_FUNCTION_ARGS) +{ + text *str = PG_GETARG_TEXT_PP(0); + text *pat = PG_GETARG_TEXT_PP(1); + int num = PG_GETARG_INT32(2); + int count = 0; + int len_p, len_s, i; + + char *str_p, *aux_str_p, *pat_p; + len_p = VARSIZE_ANY_EXHDR(pat); + len_s = VARSIZE_ANY_EXHDR(str); + + str_p = VARDATA_ANY(str) + len_s - 1; + + while (count < num) + { + pat_p = VARDATA_ANY(pat) + len_p - 1; + aux_str_p = str_p; + + if (len_s < len_p) + break; + + for (i = 0; i < len_p; i++) + if (*aux_str_p-- != *pat_p--) + break; + + if (i >= len_p) + { + count++; + /* found */ + str_p = aux_str_p; + len_s -= len_p; + continue; + } + break; + } + + PG_RETURN_TEXT_P(cstring_to_text_with_len(VARDATA_ANY(str),len_s)); +} + + +/**************************************************************** + * PLVstr.left + * + * Syntax: + * FUNCTION plvstr.left (string_in IN VARCHAR, + * num_in INTEGER) + * RETURN VARCHAR; + * + * Purpouse: + * Returns firs num_in charaters. You can use negative num_in + * left('abcde', -2) -> abc + * + ****************************************************************/ + + +Datum +plvstr_left (PG_FUNCTION_ARGS) +{ + text *str = PG_GETARG_TEXT_P(0); + int n = PG_GETARG_INT32(1); + if (n < 0) + n = ora_mb_strlen1(str) + n; + n = n < 0 ? 0 : n; + + PG_RETURN_TEXT_P(ora_substr_text(str, 1, n)); +} + + +/**************************************************************** + * PLVstr.right + * + * Syntax: + * FUNCTION plvstr.right (string_in IN VARCHAR, + * num_in INTEGER) + * RETURN VARCHAR; + * + * Purpouse: + * Returns last (right) num_in characters. + * + ****************************************************************/ + +Datum +plvstr_right (PG_FUNCTION_ARGS) +{ + text *str = PG_GETARG_TEXT_P(0); + int n = PG_GETARG_INT32(1); + if (n < 0) + n = ora_mb_strlen1(str) + n; + n = (n < 0) ? 0 : n; + + PG_RETURN_TEXT_P(ora_substr_text(str, -n, -1)); +} + +/**************************************************************** + * PLVstr.substr2 + * + * Syntax: + * FUNCTION plvstr.substr (string_in IN VARCHAR, + * start INTEGER) + * RETURN VARCHAR; + * + * Purpouse: + * Returns substring started on start_in to end + * + ****************************************************************/ + +Datum +plvstr_substr2 (PG_FUNCTION_ARGS) +{ + return oracle_substr2(fcinfo); +} + + +/**************************************************************** + * PLVstr.substr3 + * + * Syntax: + * FUNCTION plvstr.substr (string_in IN VARCHAR, + * start INTEGER, len INTEGER) + * RETURN VARCHAR; + * + * Purpouse: + * Returns len chars from start_in position + * + ****************************************************************/ + +Datum +plvstr_substr3 (PG_FUNCTION_ARGS) +{ + return oracle_substr3(fcinfo); +} + + +/**************************************************************** + * PLVchr.nth + * + * Syntax: + * FUNCTION plvchr.nth (string_in IN VARCHAR, + * nth_in IN INTEGER) + * RETURN VARCHAR; + * + * Purpouse: + * Call this function to return the Nth character in a string. + * + ****************************************************************/ + +Datum +plvchr_nth (PG_FUNCTION_ARGS) +{ + PG_RETURN_TEXT_P(ora_substr(PG_GETARG_DATUM(0), PG_GETARG_INT32(1), 1)); +} + + +/**************************************************************** + * PLVchr.first + * + * Syntax: + * FUNCTION plvchr.first (string_in IN VARCHAR, + * RETURN VARCHAR; + * + * Purpouse: + * Call this function to return the first character in a string. + * + ****************************************************************/ + +Datum +plvchr_first (PG_FUNCTION_ARGS) +{ + PG_RETURN_TEXT_P(ora_substr(PG_GETARG_DATUM(0), 1, 1)); +} + + +/**************************************************************** + * PLVchr.last + * + * Syntax: + * FUNCTION plvchr.last (string_in IN VARCHAR, + * RETURN VARCHAR; + * + * Purpouse: + * Call this function to return the last character in a string. + * + ****************************************************************/ + +Datum +plvchr_last (PG_FUNCTION_ARGS) +{ + PG_RETURN_TEXT_P(ora_substr(PG_GETARG_DATUM(0), -1, 1)); +} + + +/**************************************************************** + * PLVchr.is_blank, plvchr.is_digit, ... + * + * Syntax: + * FUNCTION plvchr.is_kind (string_in IN VARCHAR, + * kind INT) + * RETURN VARCHAR; + * + * Purpouse: + * Call this function to see if a character is blank, ... + * 1 blank, 2 digit, 3 quote, 4 other, 5 letter + * + ****************************************************************/ + +static bool +is_kind(char c, int kind) +{ + switch (kind) + { + case 1: + return c == ' '; + case 2: + return '0' <= c && c <= '9'; + case 3: + return c == '\''; + case 4: + return + (32 <= c && c <= 47) || + (58 <= c && c <= 64) || + (91 <= c && c <= 96) || (123 <= c && c <= 126); + case 5: + return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'); + default: + PARAMETER_ERROR("Second parametr isn't in enum {1,2,3,4,5}"); + return false; + } +} + +Datum +plvchr_is_kind_i (PG_FUNCTION_ARGS) +{ + int32 c = PG_GETARG_INT32(0); + int32 k = PG_GETARG_INT32(1); + + PG_RETURN_INT32(is_kind((char)c,k)); +} + +Datum +plvchr_is_kind_a (PG_FUNCTION_ARGS) +{ + text *str = PG_GETARG_TEXT_PP(0); + int32 k = PG_GETARG_INT32(1); + char c; + + NON_EMPTY_CHECK(str); + if (pg_database_encoding_max_length() > 1) + { + if (_pg_mblen(VARDATA_ANY(str)) > 1) + PG_RETURN_INT32( (k == 5) ); + } + + c = *VARDATA_ANY(str); + PG_RETURN_INT32(is_kind(c,k)); +} + + +/**************************************************************** + * PLVchr.char_name + * + * Syntax: + * FUNCTION plvchr.char_name (letter_in IN VARCHAR) + * RETURN VARCHAR; + * + * Purpouse: + * Returns the name of the character to ascii code as a VARCHAR. + * + ****************************************************************/ + +Datum +plvchr_char_name(PG_FUNCTION_ARGS) +{ + text *str = PG_GETARG_TEXT_PP(0); + text *result; + unsigned char c; + + NON_EMPTY_CHECK(str); + c = (unsigned char)*(VARDATA_ANY(str)); + + if (c >= lengthof(char_names)) + result = ora_substr_text(str, 1, 1); + else + result = cstring_to_text(char_names[c]); + + PG_RETURN_TEXT_P(result); +} + + +/**************************************************************** + * substr + * + * Syntax: + * FUNCTION substr (string, start_position, [length]) + * RETURN VARCHAR; + * + * Purpouse: + * Returns len chars from start_in position, compatible with Oracle + * + ****************************************************************/ + +Datum +oracle_substr3(PG_FUNCTION_ARGS) +{ + int32 len = PG_GETARG_INT32(2); + if (len < 0) + PG_RETURN_NULL(); + PG_RETURN_TEXT_P(ora_substr(PG_GETARG_DATUM(0), PG_GETARG_INT32(1), len)); +} + +Datum +oracle_substr2(PG_FUNCTION_ARGS) +{ + PG_RETURN_TEXT_P(ora_substr(PG_GETARG_DATUM(0), PG_GETARG_INT32(1), -1)); +} + + +static text* +ora_concat2(text *str1, text *str2) +{ + int l1; + int l2; + text *result; + + l1 = VARSIZE_ANY_EXHDR(str1); + l2 = VARSIZE_ANY_EXHDR(str2); + + result = palloc(l1+l2+VARHDRSZ); + memcpy(VARDATA(result), VARDATA_ANY(str1), l1); + memcpy(VARDATA(result) + l1, VARDATA_ANY(str2), l2); + SET_VARSIZE(result, l1 + l2 + VARHDRSZ); + + return result; +} + + +static text* +ora_concat3(text *str1, text *str2, text *str3) +{ + int l1; + int l2; + int l3; + text *result; + + l1 = VARSIZE_ANY_EXHDR(str1); + l2 = VARSIZE_ANY_EXHDR(str2); + l3 = VARSIZE_ANY_EXHDR(str3); + + result = palloc(l1+l2+l3+VARHDRSZ); + memcpy(VARDATA(result), VARDATA_ANY(str1), l1); + memcpy(VARDATA(result) + l1, VARDATA_ANY(str2), l2); + memcpy(VARDATA(result) + l1+l2, VARDATA_ANY(str3), l3); + SET_VARSIZE(result, l1 + l2 + l3 + VARHDRSZ); + + return result; +} + + +/**************************************************************** + * PLVchr.swap + * + * Syntax: + * FUNCTION swap + * (string_in IN VARCHAR2, + * replace_in IN VARCHAR2, + * start_in IN INTEGER := 1, + * oldlen_in IN INTEGER := NULL) + * RETURN VARCHAR2 + * + * Purpouse: + * Replace a substring in a string with a specified string. + * + ****************************************************************/ + +Datum +plvstr_swap(PG_FUNCTION_ARGS) +{ + text *string_in; + text *replace_in; + int start_in = 1; + int oldlen_in; + int v_len; + + if (PG_ARGISNULL(0)) + PG_RETURN_NULL(); + else + string_in = PG_GETARG_TEXT_P(0); + + if (PG_ARGISNULL(1)) + PG_RETURN_NULL(); + else + replace_in = PG_GETARG_TEXT_P(1); + + if (!PG_ARGISNULL(2)) + start_in = PG_GETARG_INT32(2); + + if (PG_ARGISNULL(3)) + oldlen_in = ora_mb_strlen1(replace_in); + else + oldlen_in = PG_GETARG_INT32(3); + + v_len = ora_mb_strlen1(string_in); + + start_in = start_in > 0 ? start_in : v_len + start_in + 1; + + if (start_in == 0 || start_in > v_len) + PG_RETURN_TEXT_P(TextPCopy(string_in)); + else if (start_in == 1) + PG_RETURN_TEXT_P(ora_concat2( + replace_in, ora_substr_text(string_in, oldlen_in+1, -1))); + else + PG_RETURN_TEXT_P(ora_concat3( + ora_substr_text(string_in, 1, start_in - 1), + replace_in, + ora_substr_text(string_in, start_in + oldlen_in, -1))); +} + +/**************************************************************** + * PLVchr.betwn + * + * Find the Substring Between Start and End Locations + * + * Syntax: + * FUNCTION plvstr.betwn (string_in IN VARCHAR2, + * start_in IN INTEGER, + * end_in IN INTEGER, + * inclusive IN BOOLEAN := TRUE) + * RETURN VARCHAR2; + * + * FUNCTION plvstr.betwn (string_in IN VARCHAR2, + * start_in IN VARCHAR2, + * end_in IN VARCHAR2 := NULL, + * startnth_in IN INTEGER := 1, + * endnth_in IN INTEGER := 1, + * inclusive IN BOOLEAN := TRUE, + * gotoend IN BOOLEAN := FALSE) + * RETURN VARCHAR2; + * + * Purpouse: + * Call this function to extract a sub-string from a string. This + * function is overloaded. You can either provide the start and end + * locations or you can provide start and end substrings. + * + ****************************************************************/ + + +Datum +plvstr_betwn_i(PG_FUNCTION_ARGS) +{ + text *string_in = PG_GETARG_TEXT_P(0); + int start_in = PG_GETARG_INT32(1); + int end_in = PG_GETARG_INT32(2); + bool inclusive = PG_GETARG_BOOL(3); + + if ((start_in < 0 && end_in > 0) || + (start_in > 0 && end_in < 0) || + (start_in > end_in)) + PARAMETER_ERROR("Wrong positions."); + + if (start_in < 0) + { + int v_len = ora_mb_strlen1(string_in); + start_in = v_len + start_in + 1; + end_in = v_len + start_in + 1; + } + + if (!inclusive) + { + start_in += 1; + end_in -= 1; + + if (start_in > end_in) + PG_RETURN_TEXT_P(cstring_to_text("")); + } + + PG_RETURN_TEXT_P(ora_substr_text(string_in, + start_in, + end_in - start_in + 1)); +} + + +Datum +plvstr_betwn_c(PG_FUNCTION_ARGS) +{ + text *string_in; + text *start_in; + text *end_in; + int startnth_in; + int endnth_in; + bool inclusive; + bool gotoend; + + int v_start; + int v_end; + + if (PG_ARGISNULL(0) || PG_ARGISNULL(1) || + PG_ARGISNULL(3) || PG_ARGISNULL(4) || + PG_ARGISNULL(5) || PG_ARGISNULL(6)) + PG_RETURN_NULL(); + + + string_in = PG_GETARG_TEXT_P(0); + start_in = PG_GETARG_TEXT_P(1); + end_in = PG_ARGISNULL(2) ? start_in : PG_GETARG_TEXT_P(2); + startnth_in = PG_GETARG_INT32(3); + endnth_in = PG_GETARG_INT32(4); + inclusive = PG_GETARG_BOOL(5); + gotoend = PG_GETARG_BOOL(6); + + if (startnth_in == 0) + { + v_start = 1; + v_end = ora_instr(string_in, end_in, 1, endnth_in); + } + else + { + v_start = ora_instr(string_in, start_in, 1, startnth_in); + v_end = ora_instr(string_in, end_in, v_start + 1, endnth_in); + } + + if (v_start == 0) + PG_RETURN_NULL(); + + if (!inclusive) + { + if (startnth_in > 0) + v_start += ora_mb_strlen1(start_in); + + v_end -= 1; + } + else + v_end += (ora_mb_strlen1(end_in) - 1); + + if (((v_start > v_end) && (v_end > 0)) || + (v_end <= 0 && !gotoend)) + PG_RETURN_NULL(); + + if (v_end <= 0) + v_end = ora_mb_strlen1(string_in); + + PG_RETURN_TEXT_P(ora_substr_text(string_in, + v_start, + v_end - v_start + 1)); +} diff --git a/src/postgres/third-party-extensions/orafce/plvsubst.c b/src/postgres/third-party-extensions/orafce/plvsubst.c new file mode 100644 index 000000000000..4da3aa995b31 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/plvsubst.c @@ -0,0 +1,257 @@ +/* + This code implements one part of functonality of + free available library PL/Vision. Please look www.quest.com + + Original author: Steven Feuerstein, 1996 - 2002 + PostgreSQL implementation author: Pavel Stehule, 2006-2018 + + This module is under BSD Licence + + History: + 1.0. first public version 22. September 2006 + +*/ + +#include "postgres.h" +#include "utils/builtins.h" +#include "utils/numeric.h" +#include "string.h" +#include "stdlib.h" +#include "utils/pg_locale.h" +#include "mb/pg_wchar.h" +#include "lib/stringinfo.h" + +#include "catalog/pg_type.h" +#include "libpq/pqformat.h" +#include "utils/array.h" +#include "utils/memutils.h" +#include "utils/lsyscache.h" +#include "access/tupmacs.h" +#include "orafce.h" +#include "builtins.h" + +PG_FUNCTION_INFO_V1(plvsubst_string_array); +PG_FUNCTION_INFO_V1(plvsubst_string_string); +PG_FUNCTION_INFO_V1(plvsubst_setsubst); +PG_FUNCTION_INFO_V1(plvsubst_setsubst_default); +PG_FUNCTION_INFO_V1(plvsubst_subst); + +#define C_SUBST "%s" + + +text *c_subst = NULL; + +static void +init_c_subst() +{ + if (!c_subst) + { + MemoryContext oldctx; + + oldctx = MemoryContextSwitchTo(TopMemoryContext); + c_subst = cstring_to_text(C_SUBST); + MemoryContextSwitchTo(oldctx); + } +} + +static void +set_c_subst(text *sc) +{ + MemoryContext oldctx; + + if (c_subst) + pfree(c_subst); + + oldctx = MemoryContextSwitchTo(TopMemoryContext); + c_subst = sc ? TextPCopy(sc) : cstring_to_text(C_SUBST); + MemoryContextSwitchTo(oldctx); +} + +static text* +plvsubst_string(text *template_in, ArrayType *vals_in, text *c_subst, FunctionCallInfo fcinfo) +{ + ArrayType *v = vals_in; + int nitems, + *dims, + ndims; + char *p; + int16 typlen; + bool typbyval; + char typalign; + char typdelim; + Oid typelem; + Oid typiofunc; + FmgrInfo proc; + int i = 0, items = 0; + StringInfo sinfo; + const char *template_str; + int template_len; + char *sizes; + int *positions; + int subst_mb_len; + int subst_len; + const bits8 *bitmap; + int bitmask; + + if (v != NULL && (ndims = ARR_NDIM(v)) > 0) + { + if (ndims != 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid parameter"), + errdetail("Array of arguments has wrong dimension: %d", ndims))); + + p = (char *) ARR_DATA_PTR(v); + dims = ARR_DIMS(v); + nitems = ArrayGetNItems(ndims, dims); + bitmap = ARR_NULLBITMAP(v); + get_type_io_data(ARR_ELEMTYPE(v), IOFunc_output, + &typlen, &typbyval, + &typalign, &typdelim, + &typelem, &typiofunc); + fmgr_info_cxt(typiofunc, &proc, fcinfo->flinfo->fn_mcxt); + } + else + { + nitems = 0; + p = NULL; + bitmap = NULL; + } + + template_str = VARDATA(template_in); + template_len = ora_mb_strlen(template_in, &sizes, &positions); + subst_mb_len = ora_mb_strlen1(c_subst); + subst_len = VARSIZE_ANY_EXHDR(c_subst); + sinfo = makeStringInfo(); + + bitmask = 1; + for (i = 0; i < template_len; i++) + { + if (strncmp(&template_str[positions[i]], VARDATA(c_subst), subst_len) == 0) + { + Datum itemvalue; + char *value; + + if (items++ < nitems) + { + if (bitmap && (*bitmap & bitmask) == 0) + value = pstrdup("NULL"); + else + { + itemvalue = fetch_att(p, typbyval, typlen); + value = DatumGetCString(FunctionCall3(&proc, + itemvalue, + ObjectIdGetDatum(typelem), + Int32GetDatum(-1))); + + p = att_addlength_pointer(p, typlen, p); + p = (char *) att_align_nominal(p, typalign); + } + appendStringInfoString(sinfo, value); + pfree(value); + + if (bitmap) + { + bitmask <<= 1; + if (bitmask == 0x100) + { + bitmap++; + bitmask = 1; + } + } + } + else + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("too few parameters specified for template string"))); + + i += subst_mb_len - 1; + } + else + appendBinaryStringInfo(sinfo, &template_str[positions[i]], sizes[i]); + } + + return cstring_to_text(sinfo->data); +} + + +Datum +plvsubst_string_array(PG_FUNCTION_ARGS) +{ + init_c_subst(); + + if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) + PG_RETURN_NULL(); + + PG_RETURN_TEXT_P(plvsubst_string(PG_GETARG_TEXT_P(0), + PG_GETARG_ARRAYTYPE_P(1), + PG_ARGISNULL(2) ? c_subst : PG_GETARG_TEXT_P(2), + fcinfo)); +} + +Datum +plvsubst_string_string(PG_FUNCTION_ARGS) +{ + Datum r; + ArrayType *array; + + LOCAL_FCINFO(locfcinfo, 2); + + Oid collation = PG_GET_COLLATION(); + + init_c_subst(); + + if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) + PG_RETURN_NULL(); + + /* + * I can't use DirectFunctionCall2 + */ + + InitFunctionCallInfoData(*locfcinfo, fcinfo->flinfo, 2, collation, NULL, NULL); + + locfcinfo->args[0].value = PG_GETARG_DATUM(1); + locfcinfo->args[1].value = PG_GETARG_IF_EXISTS(2, DATUM, CStringGetTextDatum(",")); + locfcinfo->args[0].isnull = false; + locfcinfo->args[1].isnull = false; + + r = text_to_array(locfcinfo); + + if (locfcinfo->isnull || r == (Datum) 0) + array = NULL; + else + array = DatumGetArrayTypeP(r); + + PG_RETURN_TEXT_P(plvsubst_string(PG_GETARG_TEXT_P(0), + array, + PG_GETARG_IF_EXISTS(3, TEXT_P, c_subst), + fcinfo)); +} + +Datum +plvsubst_setsubst(PG_FUNCTION_ARGS) +{ + if (PG_ARGISNULL(0)) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("substition is NULL"), + errdetail("Substitution keyword may not be NULL."))); + + set_c_subst(PG_GETARG_TEXT_P(0)); + PG_RETURN_VOID(); +} + +Datum +plvsubst_setsubst_default(PG_FUNCTION_ARGS) +{ + set_c_subst(NULL); + PG_RETURN_VOID(); +} + + +Datum +plvsubst_subst(PG_FUNCTION_ARGS) +{ + init_c_subst(); + PG_RETURN_TEXT_P(TextPCopy(c_subst)); +} diff --git a/src/postgres/third-party-extensions/orafce/putline.c b/src/postgres/third-party-extensions/orafce/putline.c new file mode 100644 index 000000000000..59b64469cddf --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/putline.c @@ -0,0 +1,366 @@ +#include "postgres.h" +#include "funcapi.h" +#include "access/heapam.h" +#include "access/htup_details.h" +#include "catalog/pg_type.h" +#include "lib/stringinfo.h" + +#undef USE_SSL +#undef ENABLE_GSS +#include "libpq/libpq.h" +#include "libpq/pqformat.h" +#include "utils/memutils.h" +#include "utils/array.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" + +#include "orafce.h" +#include "builtins.h" + +#if defined(WIN32) && !defined(_MSC_VER) +extern PGDLLIMPORT ProtocolVersion FrontendProtocol; /* for mingw */ +#endif + +/* + * TODO: BUFSIZE_UNLIMITED to be truely unlimited (or INT_MAX), + * and allocate buffers on-demand. + */ +#define BUFSIZE_DEFAULT 20000 +#define BUFSIZE_MIN 2000 +#define BUFSIZE_MAX 1000000 +#define BUFSIZE_UNLIMITED BUFSIZE_MAX + +static bool is_server_output = false; +static char *buffer = NULL; +static int buffer_size = 0; /* allocated bytes in buffer */ +static int buffer_len = 0; /* used bytes in buffer */ +static int buffer_get = 0; /* retrieved bytes in buffer */ + +static void add_str(const char *str, int len); +static void add_text(text *str); +static void add_newline(void); +static void send_buffer(void); + +/* + * Aux. buffer functionality + */ +static void +add_str(const char *str, int len) +{ + /* Discard all buffers if get_line was called. */ + if (buffer_get > 0) + { + buffer_get = 0; + buffer_len = 0; + } + + if (buffer_len + len > buffer_size) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_RESOURCES), + errmsg("buffer overflow"), + errdetail("Buffer overflow, limit of %d bytes", buffer_size), + errhint("Increase buffer size in dbms_output.enable() next time"))); + + memcpy(buffer + buffer_len, str, len); + buffer_len += len; + buffer[buffer_len] = '\0'; +} + +static void +add_text(text *str) +{ + add_str(VARDATA_ANY(str), VARSIZE_ANY_EXHDR(str)); +} + +static void +add_newline(void) +{ + add_str("", 1); /* add \0 */ + if (is_server_output) + send_buffer(); +} + + +static void +send_buffer() +{ + if (buffer_len > 0) + { + StringInfoData msgbuf; + char *cursor = buffer; + + while (--buffer_len > 0) + { + if (*cursor == '\0') + *cursor = '\n'; + cursor++; + } + + if (*cursor != '\0') + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("internal error"), + errdetail("Wrong message format detected"))); + + pq_beginmessage(&msgbuf, 'N'); + + /* + * FrontendProtocol is not avalilable in MSVC because it is not + * PGDLLEXPORT'ed. So, we assume always the protocol >= 3. + */ + +#ifndef _MSC_VER + + if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3) + { + +#endif + + pq_sendbyte(&msgbuf, PG_DIAG_MESSAGE_PRIMARY); + pq_sendstring(&msgbuf, buffer); + pq_sendbyte(&msgbuf, '\0'); + +#ifndef _MSC_VER + + } + else + { + *cursor++ = '\n'; + *cursor = '\0'; + pq_sendstring(&msgbuf, buffer); + } + +#endif + + pq_endmessage(&msgbuf); + pq_flush(); + } +} + + +/* + * Aux db functions + * + */ + +static void +dbms_output_enable_internal(int32 n_buf_size) +{ + /* We allocate +2 bytes for an end-of-line and a string terminator. */ + if (buffer == NULL) + { + buffer = MemoryContextAlloc(TopMemoryContext, n_buf_size + 2); + buffer_size = n_buf_size; + buffer_len = 0; + buffer_get = 0; + } + else if (n_buf_size > buffer_len) + { + /* We cannot shrink buffer less than current length. */ + buffer = repalloc(buffer, n_buf_size + 2); + buffer_size = n_buf_size; + } +} + +PG_FUNCTION_INFO_V1(dbms_output_enable_default); + +Datum +dbms_output_enable_default(PG_FUNCTION_ARGS) +{ + dbms_output_enable_internal(BUFSIZE_DEFAULT); + PG_RETURN_VOID(); +} + + +PG_FUNCTION_INFO_V1(dbms_output_enable); + +Datum +dbms_output_enable(PG_FUNCTION_ARGS) +{ + int32 n_buf_size; + + if (PG_ARGISNULL(0)) + n_buf_size = BUFSIZE_UNLIMITED; + else + { + n_buf_size = PG_GETARG_INT32(0); + + if (n_buf_size > BUFSIZE_MAX) + { + n_buf_size = BUFSIZE_MAX; + elog(WARNING, "Limit decreased to %d bytes.", BUFSIZE_MAX); + } + else if (n_buf_size < BUFSIZE_MIN) + { + n_buf_size = BUFSIZE_MIN; + elog(WARNING, "Limit increased to %d bytes.", BUFSIZE_MIN); + } + } + + dbms_output_enable_internal(n_buf_size); + PG_RETURN_VOID(); +} + +PG_FUNCTION_INFO_V1(dbms_output_disable); + +Datum +dbms_output_disable(PG_FUNCTION_ARGS) +{ + if (buffer) + pfree(buffer); + + buffer = NULL; + buffer_size = 0; + buffer_len = 0; + buffer_get = 0; + PG_RETURN_VOID(); +} + +PG_FUNCTION_INFO_V1(dbms_output_serveroutput); + +Datum +dbms_output_serveroutput(PG_FUNCTION_ARGS) +{ + is_server_output = PG_GETARG_BOOL(0); + if (is_server_output && !buffer) + dbms_output_enable_internal(BUFSIZE_DEFAULT); + PG_RETURN_VOID(); +} + + +/* + * main functions + */ + +PG_FUNCTION_INFO_V1(dbms_output_put); + +Datum +dbms_output_put(PG_FUNCTION_ARGS) +{ + if (buffer) + add_text(PG_GETARG_TEXT_PP(0)); + PG_RETURN_VOID(); +} + +PG_FUNCTION_INFO_V1(dbms_output_put_line); + +Datum +dbms_output_put_line(PG_FUNCTION_ARGS) +{ + if (buffer) + { + add_text(PG_GETARG_TEXT_PP(0)); + add_newline(); + } + PG_RETURN_VOID(); +} + +PG_FUNCTION_INFO_V1(dbms_output_new_line); + +Datum +dbms_output_new_line(PG_FUNCTION_ARGS) +{ + if (buffer) + add_newline(); + PG_RETURN_VOID(); +} + +static text * +dbms_output_next(void) +{ + if (buffer_get < buffer_len) + { + text *line = cstring_to_text(buffer + buffer_get); + buffer_get += VARSIZE_ANY_EXHDR(line) + 1; + return line; + } + else + return NULL; +} + +PG_FUNCTION_INFO_V1(dbms_output_get_line); + +Datum +dbms_output_get_line(PG_FUNCTION_ARGS) +{ + TupleDesc tupdesc; + Datum result; + HeapTuple tuple; + Datum values[2]; + bool nulls[2] = { false, false }; + text *line; + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + if ((line = dbms_output_next()) != NULL) + { + values[0] = PointerGetDatum(line); + values[1] = Int32GetDatum(0); /* 0: succeeded */ + } + else + { + nulls[0] = true; + values[1] = Int32GetDatum(1); /* 1: failed */ + } + + tuple = heap_form_tuple(tupdesc, values, nulls); + result = HeapTupleGetDatum(tuple); + + PG_RETURN_DATUM(result); +} + + +PG_FUNCTION_INFO_V1(dbms_output_get_lines); + +Datum +dbms_output_get_lines(PG_FUNCTION_ARGS) +{ + TupleDesc tupdesc; + Datum result; + HeapTuple tuple; + Datum values[2]; + bool nulls[2] = { false, false }; + text *line; + + int32 max_lines = PG_GETARG_INT32(0); + int32 n; + ArrayBuildState *astate = NULL; + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + for (n = 0; n < max_lines && (line = dbms_output_next()) != NULL; n++) + { + astate = accumArrayResult(astate, PointerGetDatum(line), false, + TEXTOID, CurrentMemoryContext); + } + + /* 0: lines as text array */ + if (n > 0) + values[0] = makeArrayResult(astate, CurrentMemoryContext); + else + { + int16 typlen; + bool typbyval; + char typalign; + ArrayType *arr; + + get_typlenbyvalalign(TEXTOID, &typlen, &typbyval, &typalign); + arr = construct_md_array( + NULL, + NULL, + 0, NULL, NULL, TEXTOID, typlen, typbyval, typalign); + values[0] = PointerGetDatum(arr); + } + + /* 1: # of lines as integer */ + values[1] = Int32GetDatum(n); + + tuple = heap_form_tuple(tupdesc, values, nulls); + result = HeapTupleGetDatum(tuple); + + PG_RETURN_DATUM(result); +} diff --git a/src/postgres/third-party-extensions/orafce/random.c b/src/postgres/third-party-extensions/orafce/random.c new file mode 100644 index 000000000000..858db955d760 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/random.c @@ -0,0 +1,363 @@ +/* + * Note - I don't find any documentation about pseudo random + * number generator used in Oracle. So the results of these + * functions should be different then native Oracle functions! + * This library is based on ANSI C implementation. + */ + +#include "postgres.h" +#include "access/hash.h" +#include "lib/stringinfo.h" +#include "utils/builtins.h" + +#include "stdlib.h" +#include "time.h" +#include +#include + +#include "orafce.h" +#include "builtins.h" + +PG_FUNCTION_INFO_V1(dbms_random_initialize); +PG_FUNCTION_INFO_V1(dbms_random_normal); +PG_FUNCTION_INFO_V1(dbms_random_random); +PG_FUNCTION_INFO_V1(dbms_random_seed_int); +PG_FUNCTION_INFO_V1(dbms_random_seed_varchar); +PG_FUNCTION_INFO_V1(dbms_random_string); +PG_FUNCTION_INFO_V1(dbms_random_terminate); +PG_FUNCTION_INFO_V1(dbms_random_value); +PG_FUNCTION_INFO_V1(dbms_random_value_range); + +/* Coefficients in rational approximations. */ +static const double a[] = +{ + -3.969683028665376e+01, + 2.209460984245205e+02, + -2.759285104469687e+02, + 1.383577518672690e+02, + -3.066479806614716e+01, + 2.506628277459239e+00 +}; + +static const double b[] = +{ + -5.447609879822406e+01, + 1.615858368580409e+02, + -1.556989798598866e+02, + 6.680131188771972e+01, + -1.328068155288572e+01 +}; + +static const double c[] = +{ + -7.784894002430293e-03, + -3.223964580411365e-01, + -2.400758277161838e+00, + -2.549732539343734e+00, + 4.374664141464968e+00, + 2.938163982698783e+00 +}; + +static const double d[] = +{ + 7.784695709041462e-03, + 3.224671290700398e-01, + 2.445134137142996e+00, + 3.754408661907416e+00 +}; + +#define LOW 0.02425 +#define HIGH 0.97575 + +static double ltqnorm(double p); + + +/* + * dbms_random.initialize (seed IN BINARY_INTEGER) + * + * Initialize package with a seed value + */ +Datum +dbms_random_initialize(PG_FUNCTION_ARGS) +{ + int seed = PG_GETARG_INT32(0); + + srand(seed); + + PG_RETURN_VOID(); +} + +/* + * dbms_random.normal() RETURN NUMBER; + * + * Returns random numbers in a standard normal distribution + */ +Datum +dbms_random_normal(PG_FUNCTION_ARGS) +{ + float8 result; + + /* need random value from (0..1) */ + result = ltqnorm(((double) rand() + 1) / ((double) RAND_MAX + 2)); + + PG_RETURN_FLOAT8(result); +} + +/* + * dbms_random.random() RETURN BINARY_INTEGER; + * + * Generate Random Numeric Values + */ +Datum +dbms_random_random(PG_FUNCTION_ARGS) +{ + int result; + /* + * Oracle generator generates numebers from -2^31 and +2^31, + * ANSI C only from 0 .. RAND_MAX, + */ + result = 2 * (rand() - RAND_MAX / 2); + + PG_RETURN_INT32(result); +} + +/* + * dbms_random.seed(val IN BINARY_INTEGER); + * dbms_random.seed(val IN VARCHAR2); + * + * Reset the seed value + */ +Datum +dbms_random_seed_int(PG_FUNCTION_ARGS) +{ + int seed = PG_GETARG_INT32(0); + + srand(seed); + + PG_RETURN_VOID(); +} + +/* + * Atention! + * + * Hash function should be changed between mayor pg versions, + * don't use text based seed for regres tests! + */ +Datum +dbms_random_seed_varchar(PG_FUNCTION_ARGS) +{ + text *key = PG_GETARG_TEXT_P(0); + Datum seed; + + seed = hash_any((unsigned char *) VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key)); + + srand((int) seed); + + PG_RETURN_VOID(); +} + +/* + * dbms_random.string(opt IN CHAR, len IN NUMBER) RETURN VARCHAR2; + * + * Create Random Strings + * opt seed values: + * 'a','A' alpha characters only (mixed case) + * 'l','L' lower case alpha characters only + * 'p','P' any printable characters + * 'u','U' upper case alpha characters only + * 'x','X' any alpha-numeric characters (upper) + */ +static text * +random_string(const char *charset, size_t chrset_size, int len) +{ + StringInfo str; + int i; + + str = makeStringInfo(); + for (i = 0; i < len; i++) + { + int pos = (int) ((double) rand() / ((double) RAND_MAX + 1) * chrset_size); + + appendStringInfoChar(str, charset[pos]); + } + + return cstring_to_text(str->data); +} + +Datum +dbms_random_string(PG_FUNCTION_ARGS) +{ + char *option; + int len; + const char *charset; + size_t chrset_size; + + const char *alpha_mixed = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + const char *lower_only = "abcdefghijklmnopqrstuvwxyz"; + const char *upper_only = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + const char *upper_alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + const char *printable = "`1234567890-=qwertyuiop[]asdfghjkl;'zxcvbnm,./!@#$%^&*()_+QWERTYUIOP{}|ASDFGHJKL:\"ZXCVVBNM<>? "; + + if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("an argument is NULL"))); + + option = text_to_cstring(PG_GETARG_TEXT_P(0)); + len = PG_GETARG_INT32(1); + + switch (option[0]) + { + case 'a': + case 'A': + charset = alpha_mixed; + chrset_size = strlen(alpha_mixed); + break; + case 'l': + case 'L': + charset = lower_only; + chrset_size = strlen(lower_only); + break; + case 'u': + case 'U': + charset = upper_only; + chrset_size = strlen(upper_only); + break; + case 'x': + case 'X': + charset = upper_alphanum; + chrset_size = strlen(upper_alphanum); + break; + case 'p': + case 'P': + charset = printable; + chrset_size = strlen(printable); + break; + + default: + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("unknown option '%s'", option), + errhint("available option \"aAlLuUxXpP\""))); + /* be compiler a quiete */ + charset = NULL; + chrset_size = 0; + } + + PG_RETURN_TEXT_P(random_string(charset, chrset_size, len)); +} + +/* + * dbms_random.terminate; + * + * Terminate use of the Package + */ +Datum +dbms_random_terminate(PG_FUNCTION_ARGS) +{ + /* do nothing */ + PG_RETURN_VOID(); +} + +/* + * dbms_random.value() RETURN NUMBER; + * + * Gets a random number, greater than or equal to 0 and less than 1. + */ +Datum +dbms_random_value(PG_FUNCTION_ARGS) +{ + float8 result; + + /* result [0.0 - 1.0) */ + result = (double) rand() / ((double) RAND_MAX + 1); + + PG_RETURN_FLOAT8(result); +} + +/* + * dbms_random.value(low NUMBER, high NUMBER) RETURN NUMBER + * + * Alternatively, you can get a random Oracle number x, + * where x is greater than or equal to low and less than high + */ +Datum +dbms_random_value_range(PG_FUNCTION_ARGS) +{ + float8 low = PG_GETARG_FLOAT8(0); + float8 high = PG_GETARG_FLOAT8(1); + float8 result; + + if (low > high) + PG_RETURN_NULL(); + + result = ((double) rand() / ((double) RAND_MAX + 1)) * ( high - low) + low; + + PG_RETURN_FLOAT8(result); +} + + +/* + * Lower tail quantile for standard normal distribution function. + * + * This function returns an approximation of the inverse cumulative + * standard normal distribution function. I.e., given P, it returns + * an approximation to the X satisfying P = Pr{Z <= X} where Z is a + * random variable from the standard normal distribution. + * + * The algorithm uses a minimax approximation by rational functions + * and the result has a relative error whose absolute value is less + * than 1.15e-9. + * + * Author: Peter J. Acklam + * Time-stamp: 2002-06-09 18:45:44 +0200 + * E-mail: jacklam@math.uio.no + * WWW URL: http://www.math.uio.no/~jacklam + * + * C implementation adapted from Peter's Perl version + */ +static double +ltqnorm(double p) +{ + double q, r; + + errno = 0; + + if (p < 0 || p > 1) + { + errno = EDOM; + return 0.0; + } + else if (p == 0) + { + errno = ERANGE; + return -HUGE_VAL /* minus "infinity" */; + } + else if (p == 1) + { + errno = ERANGE; + return HUGE_VAL /* "infinity" */; + } + else if (p < LOW) + { + /* Rational approximation for lower region */ + q = sqrt(-2*log(p)); + return (((((c[0]*q+c[1])*q+c[2])*q+c[3])*q+c[4])*q+c[5]) / + ((((d[0]*q+d[1])*q+d[2])*q+d[3])*q+1); + } + else if (p > HIGH) + { + /* Rational approximation for upper region */ + q = sqrt(-2*log(1-p)); + return -(((((c[0]*q+c[1])*q+c[2])*q+c[3])*q+c[4])*q+c[5]) / + ((((d[0]*q+d[1])*q+d[2])*q+d[3])*q+1); + } + else + { + /* Rational approximation for central region */ + q = p - 0.5; + r = q*q; + return (((((a[0]*r+a[1])*r+a[2])*r+a[3])*r+a[4])*r+a[5])*q / + (((((b[0]*r+b[1])*r+b[2])*r+b[3])*r+b[4])*r+1); + } +} diff --git a/src/postgres/third-party-extensions/orafce/replace_empty_string.c b/src/postgres/third-party-extensions/orafce/replace_empty_string.c new file mode 100644 index 000000000000..365641d58b80 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/replace_empty_string.c @@ -0,0 +1,339 @@ +#include "postgres.h" + +#include "access/htup_details.h" +#include "catalog/pg_type.h" +#include "commands/trigger.h" +#include "executor/spi.h" +#include "miscadmin.h" +#include "parser/parse_coerce.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" + +#include "orafce.h" +#include "builtins.h" + +PG_FUNCTION_INFO_V1(orafce_replace_empty_strings); +PG_FUNCTION_INFO_V1(orafce_replace_null_strings); + +#if PG_VERSION_NUM < 100000 + +static HeapTuple +heap_modify_tuple_by_cols(HeapTuple tuple, + TupleDesc tupleDesc, + int nCols, + int *replCols, + Datum *replValues, + bool *replIsnull) +{ + int numberOfAttributes = tupleDesc->natts; + Datum *values; + bool *isnull; + HeapTuple newTuple; + int i; + + /* + * allocate and fill values and isnull arrays from the tuple, then replace + * selected columns from the input arrays. + */ + values = (Datum *) palloc(numberOfAttributes * sizeof(Datum)); + isnull = (bool *) palloc(numberOfAttributes * sizeof(bool)); + + heap_deform_tuple(tuple, tupleDesc, values, isnull); + + for (i = 0; i < nCols; i++) + { + int attnum = replCols[i]; + + if (attnum <= 0 || attnum > numberOfAttributes) + elog(ERROR, "invalid column number %d", attnum); + values[attnum - 1] = replValues[i]; + isnull[attnum - 1] = replIsnull[i]; + } + + /* + * create a new tuple from the values and isnull arrays + */ + newTuple = heap_form_tuple(tupleDesc, values, isnull); + + pfree(values); + pfree(isnull); + + /* + * copy the identification info of the old tuple: t_ctid, t_self, and OID + * (if any) + */ + newTuple->t_data->t_ctid = tuple->t_data->t_ctid; + newTuple->t_self = tuple->t_self; + newTuple->t_tableOid = tuple->t_tableOid; + if (tupleDesc->tdhasoid) + HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple)); + + return newTuple; +} + +#endif + +static void +trigger_sanity_check(FunctionCallInfo fcinfo, const char *fname) +{ + TriggerData *trigdata = (TriggerData *) fcinfo->context; + + /* sanity checks from autoinc.c */ + if (!CALLED_AS_TRIGGER(fcinfo)) + elog(ERROR, "%s: not fired by trigger manager", fname); + + if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) + elog(ERROR, "%s: must be fired for row", fname); + + if (!TRIGGER_FIRED_BEFORE(trigdata->tg_event)) + elog(ERROR, "%s: must be fired before event", fname); + + if (trigdata->tg_trigger->tgnargs > 1) + elog(ERROR, "%s: only one trigger parameter is allowed", fname); +} + +static HeapTuple +get_rettuple(FunctionCallInfo fcinfo) +{ + TriggerData *trigdata = (TriggerData *) fcinfo->context; + HeapTuple rettuple = NULL; + + if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event)) + rettuple = trigdata->tg_trigtuple; + else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) + rettuple = trigdata->tg_newtuple; + else + /* internal error */ + elog(ERROR, "remove_empty_string: cannot process DELETE events"); + + return rettuple; +} + +/* + * Trigger argument is used as parameter that can enforce warning about modified + * columns. When first argument is "on" or "true", then warnings will be raised. + */ +static bool +should_raise_warnings(FunctionCallInfo fcinfo) +{ + TriggerData *trigdata = (TriggerData *) fcinfo->context; + Trigger *trigger = trigdata->tg_trigger; + + if (trigger->tgnargs > 0) + { + char **args = trigger->tgargs; + + if (strcmp(args[0], "on") == 0 || + strcmp(args[0], "true") == 0) + return true; + } + + return false; +} + +/* + * Detects emty strings in type text based fields and replaces them by NULL. + */ +Datum +orafce_replace_empty_strings(PG_FUNCTION_ARGS) +{ + TriggerData *trigdata = (TriggerData *) fcinfo->context; + HeapTuple rettuple = NULL; + TupleDesc tupdesc; + int *resetcols = NULL; + Datum *values = NULL; + bool *nulls = NULL; + Oid prev_typid = InvalidOid; + bool is_string = false; + int nresetcols = 0; + int attnum; + bool raise_warning = false; + char *relname = NULL; + + trigger_sanity_check(fcinfo, "replace_empty_strings"); + raise_warning = should_raise_warnings(fcinfo); + + rettuple = get_rettuple(fcinfo); + tupdesc = trigdata->tg_relation->rd_att; + + /* iterate over record's fields */ + for (attnum = 1; attnum <= tupdesc->natts; attnum++) + { + Oid typid; + + /* simple cache - lot of time columns with same type is side by side */ + typid = SPI_gettypeid(tupdesc, attnum); + if (typid != prev_typid) + { + TYPCATEGORY category; + bool ispreferred; + Oid base_typid; + + base_typid = getBaseType(typid); + get_type_category_preferred(base_typid, &category, &ispreferred); + + is_string = (category == TYPCATEGORY_STRING); + prev_typid = typid; + } + + if (is_string) + { + Datum value; + bool isnull; + + value = SPI_getbinval(rettuple, tupdesc, attnum, &isnull); + if (!isnull) + { + text *txt = DatumGetTextP(value); + + /* is it empty string (has zero length */ + if (VARSIZE_ANY_EXHDR(txt) == 0) + { + if (!resetcols) + { + /* lazy allocation of dynamic memory */ + resetcols = palloc0(tupdesc->natts * sizeof(int)); + nulls = palloc0(tupdesc->natts * sizeof(bool)); + values = palloc0(tupdesc->natts * sizeof(Datum)); + } + + resetcols[nresetcols] = attnum; + values[nresetcols] = (Datum) 0; + nulls[nresetcols++] = true; + + if (raise_warning) + { + if (!relname) + relname = SPI_getrelname(trigdata->tg_relation); + + elog(WARNING, + "Field \"%s\" of table \"%s\" is empty string (replaced by NULL).", + SPI_fname(tupdesc, attnum), relname); + } + } + } + } + } + + if (nresetcols > 0) + { + /* construct new tuple */ + rettuple = heap_modify_tuple_by_cols(rettuple, tupdesc, + nresetcols, resetcols, + values, nulls); + } + + if (relname) + pfree(relname); + if (resetcols) + pfree(resetcols); + if (values) + pfree(values); + if (nulls) + pfree(nulls); + + return PointerGetDatum(rettuple); +} + +/* + * Detects NULL in type text based fields and replaces them by empty string + */ +Datum +orafce_replace_null_strings(PG_FUNCTION_ARGS) +{ + TriggerData *trigdata = (TriggerData *) fcinfo->context; + HeapTuple rettuple = NULL; + TupleDesc tupdesc; + int *resetcols = NULL; + Datum *values = NULL; + bool *nulls = NULL; + Oid prev_typid = InvalidOid; + bool is_string = false; + int nresetcols = 0; + int attnum; + bool raise_warning = false; + char *relname = NULL; + + trigger_sanity_check(fcinfo, "replace_null_strings"); + raise_warning = should_raise_warnings(fcinfo); + + rettuple = get_rettuple(fcinfo); + + /* return fast when there are not any NULL */ + if (!HeapTupleHasNulls(rettuple)) + return PointerGetDatum(rettuple); + + tupdesc = trigdata->tg_relation->rd_att; + + /* iterate over record's fields */ + for (attnum = 1; attnum <= tupdesc->natts; attnum++) + { + Oid typid; + + /* simple cache - lot of time columns with same type is side by side */ + typid = SPI_gettypeid(tupdesc, attnum); + if (typid != prev_typid) + { + TYPCATEGORY category; + bool ispreferred; + Oid base_typid; + + base_typid = getBaseType(typid); + get_type_category_preferred(base_typid, &category, &ispreferred); + + is_string = (category == TYPCATEGORY_STRING); + prev_typid = typid; + } + + if (is_string) + { + bool isnull; + + (void) SPI_getbinval(rettuple, tupdesc, attnum, &isnull); + if (isnull) + { + if (!resetcols) + { + /* lazy allocation of dynamic memory */ + resetcols = palloc0(tupdesc->natts * sizeof(int)); + nulls = palloc0(tupdesc->natts * sizeof(bool)); + values = palloc0(tupdesc->natts * sizeof(Datum)); + } + + resetcols[nresetcols] = attnum; + values[nresetcols] = PointerGetDatum(cstring_to_text_with_len("", 0)); + nulls[nresetcols++] = false; + + if (raise_warning) + { + if (!relname) + relname = SPI_getrelname(trigdata->tg_relation); + + elog(WARNING, + "Field \"%s\" of table \"%s\" is NULL (replaced by '').", + SPI_fname(tupdesc, attnum), relname); + } + } + } + } + + if (nresetcols > 0) + { + /* construct new tuple */ + rettuple = heap_modify_tuple_by_cols(rettuple, tupdesc, + nresetcols, resetcols, + values, nulls); + } + + if (relname) + pfree(relname); + if (resetcols) + pfree(resetcols); + if (values) + pfree(values); + if (nulls) + pfree(nulls); + + return PointerGetDatum(rettuple); +} diff --git a/src/postgres/third-party-extensions/orafce/shmmc.c b/src/postgres/third-party-extensions/orafce/shmmc.c new file mode 100644 index 000000000000..a51f5e508905 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/shmmc.c @@ -0,0 +1,347 @@ +/* + * + * Shared memory control - based on alocating chunks aligned on + * asize array (fibonachi), and dividing free bigger block. + * + */ + +#include "postgres.h" +#include "shmmc.h" +#include "stdlib.h" +#include "string.h" +#include "orafce.h" + +#include "stdint.h" + + +#define LIST_ITEMS 512 + +int context; + +typedef struct { + size_t size; + void* first_byte_ptr; + bool dispossible; +/* int16 context; */ +} list_item; + +typedef struct { + int list_c; + size_t max_size; + vardata data[1]; /* flexible array member */ +} mem_desc; + +#define MAX_SIZE 82688 + +static size_t asize[] = { + 32, + 64, 96, 160, 256, + 416, 672, 1088, 1760, + 2848, 4608, 7456, 12064, + 19520, 31584, 51104, 82688}; + + +int *list_c = NULL; +list_item *list = NULL; +size_t max_size; + +int cycle = 0; + + +/* align requested size */ + +static int +ptr_comp(const void* a, const void* b) +{ + ptrdiff_t d; + + list_item *_a = (list_item*) a; + list_item *_b = (list_item*) b; + + d = (uintptr_t)_a->first_byte_ptr - (uintptr_t)_b->first_byte_ptr; + + return d > 0 ? 1 : (d < 0 ? -1 : 0); +} + +char * +ora_sstrcpy(char *str) +{ + size_t len; + char *result; + + len = strlen(str); + if (NULL != (result = ora_salloc(len+1))) + memcpy(result, str, len + 1); + else + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"), + errdetail("Failed while allocation block %d bytes in shared memory.", (int) len+1), + errhint("Increase SHMEMMSGSZ and recompile package."))); + + return result; +} + +char * +ora_scstring(text *str) +{ + int len; + char *result; + + len = VARSIZE_ANY_EXHDR(str); + + if (NULL != (result = ora_salloc(len+1))) + { + memcpy(result, VARDATA_ANY(str), len); + result[len] = '\0'; + } + else + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"), + errdetail("Failed while allocation block %d bytes in shared memory.", (int) len+1), + errhint("Increase SHMEMMSGSZ and recompile package."))); + + return result; +} + +/* + * Compact the list of slots, by merging adjacent unused slots into larger + * slots. + */ +static void +defragmentation() +{ + int src, target; + + /* Sort the array to pointer order */ + qsort(list, *list_c, sizeof(list_item), ptr_comp); + + /* Merge adjacent dispossible slots, and move up other slots */ + target = 0; + for (src = 0; src < *list_c; src++) + { + if (target > 0 && + list[src].dispossible && + list[target - 1].dispossible) + { + list[target - 1].size += list[src].size; + } + else + { + if (src != target) + memcpy(&list[target], &list[src], sizeof(list_item)); + target++; + } + } + *list_c = target; +} + +static size_t +align_size(size_t size) +{ + int i; + + /* default, we can allocate max MAX_SIZE memory block */ + + for (i = 0; i < 17; i++) + if (asize[i] >= size) + return asize[i]; + + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("too much large memory block request"), + errdetail("Failed while allocation block %lu bytes in shared memory.", (unsigned long) size), + errhint("Increase MAX_SIZE constant, fill table a_size and recompile package."))); + + return 0; +} + +/* + initialize shared memory. It works in two modes, create and no create. + No create is used for mounting shared memory buffer. Top of memory is + used for list_item array. +*/ + +void +ora_sinit(void *ptr, size_t size, bool create) +{ + if (list == NULL) + { + mem_desc *m = (mem_desc*)ptr; + list = (list_item*)m->data; + list_c = &m->list_c; + max_size = m->max_size = size; + + if (create) + { + list[0].size = size - sizeof(list_item)*LIST_ITEMS - sizeof(mem_desc); + list[0].first_byte_ptr = ((char *) &m->data) + sizeof(list_item)*LIST_ITEMS; + list[0].dispossible = true; + *list_c = 1; + } + } +} + + +void* +ora_salloc(size_t size) +{ + size_t aligned_size; + int repeat_c; + void *ptr = NULL; + + aligned_size = align_size(size); + + for (repeat_c = 0; repeat_c < 2; repeat_c++) + { + size_t max_min = max_size; + int select = -1; + int i; + + /* find first good free block */ + for (i = 0; i < *list_c; i++) + { + if (list[i].dispossible) + { + /* If this block is just the right size, return it */ + if (list[i].size == aligned_size) + { + list[i].dispossible = false; + ptr = list[i].first_byte_ptr; + /* list[i].context = context; */ + + return ptr; + } + + if (list[i].size > aligned_size && list[i].size < max_min) + { + max_min = list[i].size; + select = i; + } + } + } + + /* If no suitable free slot found, defragment and try again. */ + if (select == -1 || *list_c == LIST_ITEMS) + { + defragmentation(); + continue; + } + + /* + * A slot larger than required was found. Divide it to avoid wasting + * space, and return the slot of the right size. + */ + list[*list_c].size = list[select].size - aligned_size; + list[*list_c].first_byte_ptr = (char*)list[select].first_byte_ptr + aligned_size; + list[*list_c].dispossible = true; + list[select].size = aligned_size; + list[select].dispossible = false; + /* list[select].context = context; */ + ptr = list[select].first_byte_ptr; + *list_c += 1; + break; + } + + return ptr; +} + +void +ora_sfree(void* ptr) +{ + int i; + +/* + if (cycle++ % 100 == 0) + { + size_t suma = 0; + for (i = 0; i < *list_c; i++) + if (list[i].dispossible) + suma += list[i].size; + elog(NOTICE, "=============== FREE MEM REPORT === %10d ================", suma); + } +*/ + + for (i = 0; i < *list_c; i++) + if (list[i].first_byte_ptr == ptr) + { + list[i].dispossible = true; + /* list[i].context = -1; */ + memset(list[i].first_byte_ptr, '#', list[i].size); + return; + } + + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("corrupted pointer"), + errdetail("Failed while reallocating memory block in shared memory."), + errhint("Report this bug to autors."))); +} + + +void* +ora_srealloc(void *ptr, size_t size) +{ + void *result; + size_t aux_s = 0; + int i; + + for (i = 0; i < *list_c; i++) + if (list[i].first_byte_ptr == ptr) + { + if (align_size(size) <= list[i].size) + return ptr; + aux_s = list[i].size; + } + + if (aux_s == 0) + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("corrupted pointer"), + errdetail("Failed while reallocating memory block in shared memory."), + errhint("Report this bug to autors."))); + + + if (NULL != (result = ora_salloc(size))) + { + memcpy(result, ptr, aux_s); + ora_sfree(ptr); + } + + return result; +} + +/* + * alloc shared memory, raise exception if not + */ + +void* +salloc(size_t size) +{ + void* result; + + if (NULL == (result = ora_salloc(size))) + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"), + errdetail("Failed while allocation block %lu bytes in shared memory.", (unsigned long) size), + errhint("Increase SHMEMMSGSZ and recompile package."))); + + return result; +} + +void* +srealloc(void *ptr, size_t size) +{ + void* result; + + if (NULL == (result = ora_srealloc(ptr, size))) + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"), + errdetail("Failed while reallocation block %lu bytes in shared memory.", (unsigned long) size), + errhint("Increase SHMEMMSGSZ and recompile package."))); + + return result; +} diff --git a/src/postgres/third-party-extensions/orafce/shmmc.h b/src/postgres/third-party-extensions/orafce/shmmc.h new file mode 100644 index 000000000000..2bf26b96ae75 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/shmmc.h @@ -0,0 +1,12 @@ +#ifndef __SHMMC__ +#define __SHMMC__ + +void ora_sinit(void *ptr, size_t size, bool create); +void* ora_salloc(size_t size); +void* ora_srealloc(void *ptr, size_t size); +void ora_sfree(void* ptr); +char* ora_sstrcpy(char *str); +char* ora_scstring(text *str); +void* salloc(size_t size); +void* srealloc(void *ptr,size_t size); +#endif diff --git a/src/postgres/third-party-extensions/orafce/sql/dbms_alert_session_A.sql b/src/postgres/third-party-extensions/orafce/sql/dbms_alert_session_A.sql new file mode 100644 index 000000000000..18cbb6b60707 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/sql/dbms_alert_session_A.sql @@ -0,0 +1,66 @@ +\set ECHO all + +SELECT pg_sleep(3); + +/* + * DBMS_ALERT is used for one-way communication of one session to other. + * + * This session mainly sends signals for testing the alert functionality in + * session B and C. + * + * The following alerts are used to ensure that signals are sent at correct + * times to session B for testing. These signals are sent from session B + * indicating completion of an event. + * After the signal is received, the next required signal for testing is sent + * from this session. + */ + +SELECT dbms_alert.register('b1'); +SELECT dbms_alert.register('b2'); +SELECT dbms_alert.register('b3'); +SELECT dbms_alert.register('b4'); +SELECT dbms_alert.register('b5'); + +SELECT dbms_alert.signal('a1','Msg1 for a1'); + +SELECT dbms_alert.signal('a2','Msg1 for a2'); + +/* + * Test: defered_signal + * The signal is received only when the signalling transaction commits. + * To test this, an explict BEGIN-COMMIT block is used. + */ +SELECT dbms_alert.signal('tds','Begin defered_signal test'); +BEGIN; +SELECT dbms_alert.signal('tds','Testing defered_signal'); +/* The signal is received while transaction is running */ +SELECT dbms_alert.waitone('b1',20); +COMMIT; +/* The signal is received after transaction completed. + * After this the tds signal is received in session B indicating that the + * signal is received only after commit. + */ +SELECT dbms_alert.waitone('b1',20); + +SELECT dbms_alert.waitone('b2',20); +/* This signals a3 which is not registered in Session B */ +SELECT dbms_alert.signal('a3','Msg1 for a3'); +/* alert a4 is signalled soon after a3 */ +SELECT dbms_alert.signal('a4','Test- Register after signal'); + +/* This signal indicates at remove() is called */ +SELECT dbms_alert.waitone('b3',20); +/* Send signal which is removed in session B */ +SELECT dbms_alert.signal('a1','Msg2 for a1'); + +SELECT dbms_alert.waitone('b4',20); +/* Send signal which is registered in B and not removed */ +SELECT dbms_alert.signal('a4','Msg1 for a4'); + +/* This signal inidcates that removeall() is called */ +SELECT dbms_alert.waitone('b5',20); +/* Send a signal to test if session B receives it after removeall() */ +SELECT dbms_alert.signal('a2','Msg2 for a2'); + +/* cleanup */ +SELECT dbms_alert.removeall(); diff --git a/src/postgres/third-party-extensions/orafce/sql/dbms_alert_session_B.sql b/src/postgres/third-party-extensions/orafce/sql/dbms_alert_session_B.sql new file mode 100644 index 000000000000..f7f3008306d6 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/sql/dbms_alert_session_B.sql @@ -0,0 +1,49 @@ +\set ECHO all + +/* Register alerts */ +SELECT dbms_alert.register('a1'); +SELECT dbms_alert.register('a2'); +SELECT dbms_alert.register('tds'); + +/* Test: multisession waitone */ +SELECT dbms_alert.waitone('a1',20); + +/* Test: multisession waitany */ +SELECT dbms_alert.waitany(10); + +/* Test defered_signal */ +/* This indicated that the transaction has begun */ +SELECT dbms_alert.waitone('tds',10); +/* The signal will not be received because the transaction is running */ +SELECT dbms_alert.waitone('tds',2); +SELECT dbms_alert.signal('b1','Transaction still running'); +SELECT dbms_alert.signal('b1','Transaction committed'); +/* Since the transaction has commited, the signal will be received */ +SELECT dbms_alert.waitone('tds',10); + +/* Signal session A to send msg1 for a3 */ +SELECT dbms_alert.signal('b2','to check unregistered alert wait'); +/* Test: wait for unregistered alert which is signaled*/ +SELECT dbms_alert.waitone('a3',2); + +/* Test: Register after alert is signaled and wait */ +SELECT dbms_alert.register('a4'); +SELECT dbms_alert.waitone('a4',2); + +/* Test: remove one */ +SELECT dbms_alert.remove('a1'); +/* Signal session A to send msg2 for a1 */ +SELECT dbms_alert.signal('b3','remove(a1) called'); +/* Test: wait for removed alert */ +SELECT dbms_alert.waitone('a1',2); +/* Signal session A to send msg1 for a4 */ +SELECT dbms_alert.signal('b4','to check unremoved alert'); +/* Test: Check if unremoved alert is received */ +SELECT dbms_alert.waitone('a4',10); + +/* Test removeall */ +SELECT dbms_alert.removeall(); +/* Signal session A to send msg2 for a2 */ +SELECT dbms_alert.signal('b5','removeall called'); +/* Test: Use waitany to see if any alert is received */ +SELECT dbms_alert.waitany(2); diff --git a/src/postgres/third-party-extensions/orafce/sql/dbms_alert_session_C.sql b/src/postgres/third-party-extensions/orafce/sql/dbms_alert_session_C.sql new file mode 100644 index 000000000000..17b327b6bbd2 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/sql/dbms_alert_session_C.sql @@ -0,0 +1,15 @@ +\set ECHO all + +/* Register alerts */ +SELECT dbms_alert.register('a1'); +SELECT dbms_alert.register('a2'); + +/* Test: multisession waitone */ +SELECT dbms_alert.waitone('a1',20); + +/* Test: multisession waitany */ +SELECT dbms_alert.waitany(10); + +/* cleanup */ +SELECT dbms_alert.removeall(); + diff --git a/src/postgres/third-party-extensions/orafce/sql/dbms_pipe.sql b/src/postgres/third-party-extensions/orafce/sql/dbms_pipe.sql new file mode 100644 index 000000000000..853b9d1697b0 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/sql/dbms_pipe.sql @@ -0,0 +1,58 @@ +CREATE TYPE testt AS (x integer, y integer, v varchar); + +CREATE OR REPLACE FUNCTION st(integer, integer, varchar) +RETURNS void AS $$ +DECLARE t testt; r record; +BEGIN t.x := $1; t.y := $2; t.v := $3; + select into r 10,10,'boo'; + PERFORM dbms_pipe.pack_message(t); +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION sk() +RETURNS void AS $$ +DECLARE t testt; + o testt; +BEGIN t.x := 1; t.y := 2; t.v := 'Pavel Stehule'; + RAISE NOTICE 'SEND'; + PERFORM dbms_pipe.pack_message(t); + PERFORM dbms_pipe.send_message('boo',4,10); + RAISE NOTICE 'RECEIVE'; +-- PERFORM dbms_pipe.receive_message('boo',4); +-- SELECT INTO o * from dbms_pipe.unpack_message_record() as (x integer, y integer, v varchar); +-- RAISE NOTICE 'received %', o.v; +END; +$$ LANGUAGE plpgsql; + + + +CREATE OR REPLACE FUNCTION SessionA() RETURNS void AS $$ +BEGIN + FOR i IN 1..100000 LOOP + PERFORM dbms_pipe.pack_message('Prvni '||i); + PERFORM dbms_pipe.pack_message('Druhy '||i); + RAISE NOTICE 'SEND'; + IF dbms_pipe.send_message('pipe_name',4,10) = 1 THEN + RAISE NOTICE 'Timeout'; + PERFORM pg_sleep(5); + PERFORM dbms_pipe.send_message('pipe_name',4,10); + END IF; + PERFORM pg_sleep(random()); + END LOOP; +END; $$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION SessionB() RETURNS void AS $$ +BEGIN + FOR i IN 1..100000 LOOP + IF dbms_pipe.receive_message('pipe_name',4) = 1 THEN + RAISE NOTICE 'Timeout'; + PERFORM pg_sleep(5); + CONTINUE; + END IF; + RAISE NOTICE 'RECEIVE % %', dbms_pipe.unpack_message_text(), + dbms_pipe.unpack_message_text(); + PERFORM pg_sleep(random()); + END LOOP; +END; $$ LANGUAGE plpgsql; + + diff --git a/src/postgres/third-party-extensions/orafce/sql/init.sql b/src/postgres/third-party-extensions/orafce/sql/init.sql new file mode 100644 index 000000000000..f5a1fda2aa0a --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/sql/init.sql @@ -0,0 +1,4 @@ +\set ECHO none +set client_min_messages TO error; +CREATE EXTENSION IF NOT EXISTS orafce; +set client_min_messages TO default; \ No newline at end of file diff --git a/src/postgres/third-party-extensions/orafce/sql/nlssort.sql b/src/postgres/third-party-extensions/orafce/sql/nlssort.sql new file mode 100644 index 000000000000..9cfb2f1f279c --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/sql/nlssort.sql @@ -0,0 +1,21 @@ +-- Tests for nlssort +\set ECHO none +SET client_min_messages = error; +DROP DATABASE IF EXISTS regression_sort; +CREATE DATABASE regression_sort WITH TEMPLATE = template0 ENCODING='SQL_ASCII' LC_COLLATE='C' LC_CTYPE='C'; +\c regression_sort +SET client_min_messages = error; +CREATE EXTENSION orafce; +SET client_min_messages = default; +CREATE TABLE test_sort (name TEXT); +INSERT INTO test_sort VALUES ('red'), ('brown'), ('yellow'), ('Purple'); +SELECT * FROM test_sort ORDER BY NLSSORT(name, 'en_US.utf8'); +SELECT * FROM test_sort ORDER BY NLSSORT(name, ''); +SELECT set_nls_sort('invalid'); +SELECT * FROM test_sort ORDER BY NLSSORT(name); +SELECT set_nls_sort(''); +SELECT * FROM test_sort ORDER BY NLSSORT(name); +SELECT set_nls_sort('en_US.utf8'); +SELECT * FROM test_sort ORDER BY NLSSORT(name); +INSERT INTO test_sort VALUES(NULL); +SELECT * FROM test_sort ORDER BY NLSSORT(name); diff --git a/src/postgres/third-party-extensions/orafce/sqlparse.c b/src/postgres/third-party-extensions/orafce/sqlparse.c new file mode 100644 index 000000000000..fce8b55f8f5d --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/sqlparse.c @@ -0,0 +1,1807 @@ +/* A Bison parser, made by GNU Bison 3.5. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2019 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Undocumented macros, especially those whose name start with YY_, + are private implementation details. Do not rely on them. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "3.5" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + +/* Substitute the variable and function names. */ +#define yyparse orafce_sql_yyparse +#define yylex orafce_sql_yylex +#define yyerror orafce_sql_yyerror +#define yydebug orafce_sql_yydebug +#define yynerrs orafce_sql_yynerrs +#define yylval orafce_sql_yylval +#define yychar orafce_sql_yychar +#define yylloc orafce_sql_yylloc + +/* First part of user prologue. */ +#line 8 "sqlparse.y" + + +#define YYDEBUG 1 + +#define YYLLOC_DEFAULT(Current, Rhs, N) \ +do { \ +if (N) \ +(Current) = (Rhs)[1]; \ +else \ +(Current) = (Rhs)[0]; \ +} while (0) + +#include "postgres.h" +#include "orafce.h" +#include "plvlex.h" +#include "nodes/pg_list.h" + + +#define MOVE_TO_S(src,dest,col) dest->col = src.col ? pstrdup(src.col) : NULL +#define MOVE_TO(src,dest,col) dest->col = src.col + +#define FILL_NODE(src,dest) \ + MOVE_TO_S(src,dest,str), \ + MOVE_TO(src,dest,keycode), \ + MOVE_TO(src,dest,lloc), \ + MOVE_TO_S(src,dest,sep), \ + MOVE_TO(src,dest,modificator) + +static orafce_lexnode *__node; + +#define CREATE_NODE(src,type) \ + ( \ + __node = (orafce_lexnode*) palloc(sizeof(orafce_lexnode)), \ + __node->typenode = X_##type, \ + __node->classname = #type, \ + FILL_NODE(src,__node), \ + __node) + + +extern int yylex(void); /* defined as fdate_yylex in fdatescan.l */ + +static char *scanbuf; +static int scanbuflen; + +void orafce_sql_yyerror(List **result, const char *message); + +#define YYMALLOC malloc /* XXX: should use palloc? */ +#define YYFREE free /* XXX: should use pfree? */ + + +#line 129 "sqlparse.c" + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +/* Use api.header.include to #include this header + instead of duplicating it here. */ +#ifndef YY_ORAFCE_SQL_YY_SQLPARSE_H_INCLUDED +# define YY_ORAFCE_SQL_YY_SQLPARSE_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int orafce_sql_yydebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + X_IDENT = 258, + X_NCONST = 259, + X_SCONST = 260, + X_OP = 261, + X_PARAM = 262, + X_COMMENT = 263, + X_WHITESPACE = 264, + X_KEYWORD = 265, + X_OTHERS = 266, + X_TYPECAST = 267 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 63 "sqlparse.y" + + int ival; + orafce_lexnode *node; + List *list; + struct + { + char *str; + int keycode; + int lloc; + char *sep; + char *modificator; + } val; + +#line 208 "sqlparse.c" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + +/* Location type. */ +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE YYLTYPE; +struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +}; +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + +extern YYSTYPE orafce_sql_yylval; +extern YYLTYPE orafce_sql_yylloc; +int orafce_sql_yyparse (List **result); + +#endif /* !YY_ORAFCE_SQL_YY_SQLPARSE_H_INCLUDED */ + + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + and (if available) are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + +/* Stored state numbers (used for stacks). */ +typedef yytype_int8 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(E) ((void) (E)) +#else +# define YYUSE(E) /* empty */ +#endif + +#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ + && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; + YYLTYPE yyls_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE) \ + + YYSIZEOF (YYLTYPE)) \ + + 2 * YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 13 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 10 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 13 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 4 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 13 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 15 + +#define YYUNDEFTOK 2 +#define YYMAXUTOK 267 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_int8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12 +}; + +#if YYDEBUG + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_int8 yyrline[] = +{ + 0, 91, 91, 95, 96, 100, 101, 102, 103, 104, + 105, 106, 107, 108 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || 0 +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "X_IDENT", "X_NCONST", "X_SCONST", + "X_OP", "X_PARAM", "X_COMMENT", "X_WHITESPACE", "X_KEYWORD", "X_OTHERS", + "X_TYPECAST", "$accept", "root", "elements", "anyelement", YY_NULLPTR +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_int16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267 +}; +# endif + +#define YYPACT_NINF (-4) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-1) + +#define yytable_value_is_error(Yyn) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int8 yypact[] = +{ + -3, -4, -4, -4, -4, -4, -4, -4, -4, -4, + 9, -3, -4, -4, -4 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_int8 yydefact[] = +{ + 0, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 0, 2, 3, 1, 4 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -4, -4, -4, -1 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 10, 11, 12 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int8 yytable[] = +{ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 13, + 14 +}; + +static const yytype_int8 yycheck[] = +{ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, + 11 +}; + + /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_int8 yystos[] = +{ + 0, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 14, 15, 16, 0, 16 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_int8 yyr1[] = +{ + 0, 13, 14, 15, 15, 16, 16, 16, 16, 16, + 16, 16, 16, 16 +}; + + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +static const yytype_int8 yyr2[] = +{ + 0, 2, 1, 1, 2, 1, 1, 1, 1, 1, + 1, 1, 1, 1 +}; + + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (result, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Error token number */ +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (N) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (0) +#endif + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + +/* Print *YYLOCP on YYO. Private, do not rely on its existence. */ + +YY_ATTRIBUTE_UNUSED +static int +yy_location_print_ (FILE *yyo, YYLTYPE const * const yylocp) +{ + int res = 0; + int end_col = 0 != yylocp->last_column ? yylocp->last_column - 1 : 0; + if (0 <= yylocp->first_line) + { + res += YYFPRINTF (yyo, "%d", yylocp->first_line); + if (0 <= yylocp->first_column) + res += YYFPRINTF (yyo, ".%d", yylocp->first_column); + } + if (0 <= yylocp->last_line) + { + if (yylocp->first_line < yylocp->last_line) + { + res += YYFPRINTF (yyo, "-%d", yylocp->last_line); + if (0 <= end_col) + res += YYFPRINTF (yyo, ".%d", end_col); + } + else if (0 <= end_col && yylocp->first_column < end_col) + res += YYFPRINTF (yyo, "-%d", end_col); + } + return res; + } + +# define YY_LOCATION_PRINT(File, Loc) \ + yy_location_print_ (File, &(Loc)) + +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value, Location, result); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, List **result) +{ + FILE *yyoutput = yyo; + YYUSE (yyoutput); + YYUSE (yylocationp); + YYUSE (result); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyo, yytoknum[yytype], *yyvaluep); +# endif + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yytype); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, List **result) +{ + YYFPRINTF (yyo, "%s %s (", + yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); + + YY_LOCATION_PRINT (yyo, *yylocationp); + YYFPRINTF (yyo, ": "); + yy_symbol_value_print (yyo, yytype, yyvaluep, yylocationp, result); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, List **result) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + yystos[yyssp[yyi + 1 - yynrhs]], + &yyvsp[(yyi + 1) - (yynrhs)] + , &(yylsp[(yyi + 1) - (yynrhs)]) , result); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, yylsp, Rule, result); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen(S) (YY_CAST (YYPTRDIFF_T, strlen (S))) +# else +/* Return the length of YYSTR. */ +static YYPTRDIFF_T +yystrlen (const char *yystr) +{ + YYPTRDIFF_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +yystpcpy (char *yydest, const char *yysrc) +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYPTRDIFF_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYPTRDIFF_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + else + goto append; + + append: + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (yyres) + return yystpcpy (yyres, yystr) - yyres; + else + return yystrlen (yystr); +} +# endif + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg, + yy_state_t *yyssp, int yytoken) +{ + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULLPTR; + /* Arguments of yyformat: reported tokens (one for the "unexpected", + one per "expected"). */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Actual size of YYARG. */ + int yycount = 0; + /* Cumulated lengths of YYARG. */ + YYPTRDIFF_T yysize = 0; + + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[*yyssp]; + YYPTRDIFF_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); + yysize = yysize0; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + { + YYPTRDIFF_T yysize1 + = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return 2; + } + } + } + } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + default: /* Avoid compiler warnings. */ + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + { + /* Don't count the "%s"s in the final size, but reserve room for + the terminator. */ + YYPTRDIFF_T yysize1 = yysize + (yystrlen (yyformat) - 2 * yycount) + 1; + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return 2; + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + ++yyp; + ++yyformat; + } + } + return 0; +} +#endif /* YYERROR_VERBOSE */ + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, List **result) +{ + YYUSE (yyvaluep); + YYUSE (yylocationp); + YYUSE (result); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yytype); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + +/* The lookahead symbol. */ +int yychar; + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval; +/* Location data for the lookahead symbol. */ +YYLTYPE yylloc +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + = { 1, 1, 1, 1 } +# endif +; +/* Number of syntax errors so far. */ +int yynerrs; + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (List **result) +{ + yy_state_fast_t yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + 'yyss': related to states. + 'yyvs': related to semantic values. + 'yyls': related to locations. + + Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss; + yy_state_t *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + /* The location stack. */ + YYLTYPE yylsa[YYINITDEPTH]; + YYLTYPE *yyls; + YYLTYPE *yylsp; + + /* The locations where the error started and ended. */ + YYLTYPE yyerror_range[3]; + + YYPTRDIFF_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + YYLTYPE yyloc; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYPTRDIFF_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; + yylsp = yyls = yylsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + yylsp[0] = yylloc; + goto yysetstate; + + +/*------------------------------------------------------------. +| yynewstate -- push a new state, which is found in yystate. | +`------------------------------------------------------------*/ +yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + +/*--------------------------------------------------------------------. +| yysetstate -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + goto yyexhaustedlab; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + YYLTYPE *yyls1 = yyls; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yyls1, yysize * YYSIZEOF (*yylsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + yyls = yyls1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); + YYSTACK_RELOCATE (yyls_alloc, yyls); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + yylsp = yyls + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = yylex (); + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + *++yylsp = yylloc; + + /* Discard the shifted token. */ + yychar = YYEMPTY; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + /* Default location. */ + YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); + yyerror_range[1] = yyloc; + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: +#line 91 "sqlparse.y" + { *((void**)result) = (yyvsp[0].list); } +#line 1495 "sqlparse.c" + break; + + case 3: +#line 95 "sqlparse.y" + { (yyval.list) = list_make1((yyvsp[0].node));} +#line 1501 "sqlparse.c" + break; + + case 4: +#line 96 "sqlparse.y" + { (yyval.list) = lappend((yyvsp[-1].list), (yyvsp[0].node));} +#line 1507 "sqlparse.c" + break; + + case 5: +#line 100 "sqlparse.y" + { (yyval.node) = (orafce_lexnode*) CREATE_NODE((yyvsp[0].val), IDENT); } +#line 1513 "sqlparse.c" + break; + + case 6: +#line 101 "sqlparse.y" + { (yyval.node) = (orafce_lexnode*) CREATE_NODE((yyvsp[0].val), NCONST); } +#line 1519 "sqlparse.c" + break; + + case 7: +#line 102 "sqlparse.y" + { (yyval.node) = (orafce_lexnode*) CREATE_NODE((yyvsp[0].val), SCONST); } +#line 1525 "sqlparse.c" + break; + + case 8: +#line 103 "sqlparse.y" + { (yyval.node) = (orafce_lexnode*) CREATE_NODE((yyvsp[0].val), OP); } +#line 1531 "sqlparse.c" + break; + + case 9: +#line 104 "sqlparse.y" + { (yyval.node) = (orafce_lexnode*) CREATE_NODE((yyvsp[0].val), PARAM); } +#line 1537 "sqlparse.c" + break; + + case 10: +#line 105 "sqlparse.y" + { (yyval.node) = (orafce_lexnode*) CREATE_NODE((yyvsp[0].val), COMMENT); } +#line 1543 "sqlparse.c" + break; + + case 11: +#line 106 "sqlparse.y" + { (yyval.node) = (orafce_lexnode*) CREATE_NODE((yyvsp[0].val), WHITESPACE); } +#line 1549 "sqlparse.c" + break; + + case 12: +#line 107 "sqlparse.y" + { (yyval.node) = (orafce_lexnode*) CREATE_NODE((yyvsp[0].val), KEYWORD); } +#line 1555 "sqlparse.c" + break; + + case 13: +#line 108 "sqlparse.y" + { (yyval.node) = (orafce_lexnode*) CREATE_NODE((yyvsp[0].val), OTHERS); } +#line 1561 "sqlparse.c" + break; + + +#line 1565 "sqlparse.c" + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + *++yylsp = yyloc; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (result, YY_("syntax error")); +#else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) + { + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = YY_CAST (char *, YYSTACK_ALLOC (YY_CAST (YYSIZE_T, yymsg_alloc))); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (result, yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; + } +# undef YYSYNTAX_ERROR +#endif + } + + yyerror_range[1] = yylloc; + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, &yylloc, result); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + yyerror_range[1] = *yylsp; + yydestruct ("Error: popping", + yystos[yystate], yyvsp, yylsp, result); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + yyerror_range[2] = yylloc; + /* Using YYLLOC is tempting, but would change the location of + the lookahead. YYLOC is available though. */ + YYLLOC_DEFAULT (yyloc, yyerror_range, 2); + *++yylsp = yyloc; + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + + +#if !defined yyoverflow || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (result, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + + +/*-----------------------------------------------------. +| yyreturn -- parsing is finished, return the result. | +`-----------------------------------------------------*/ +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, &yylloc, result); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp, yylsp, result); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + return yyresult; +} +#line 110 "sqlparse.y" + + +#undef YYLTYPE + +#include "sqlscan.c" diff --git a/src/postgres/third-party-extensions/orafce/sqlparse.h b/src/postgres/third-party-extensions/orafce/sqlparse.h new file mode 100644 index 000000000000..26a783243235 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/sqlparse.h @@ -0,0 +1,110 @@ +/* A Bison parser, made by GNU Bison 3.5. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2019 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* Undocumented macros, especially those whose name start with YY_, + are private implementation details. Do not rely on them. */ + +#ifndef YY_ORAFCE_SQL_YY_SQLPARSE_H_INCLUDED +# define YY_ORAFCE_SQL_YY_SQLPARSE_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int orafce_sql_yydebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + X_IDENT = 258, + X_NCONST = 259, + X_SCONST = 260, + X_OP = 261, + X_PARAM = 262, + X_COMMENT = 263, + X_WHITESPACE = 264, + X_KEYWORD = 265, + X_OTHERS = 266, + X_TYPECAST = 267 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 63 "sqlparse.y" + + int ival; + orafce_lexnode *node; + List *list; + struct + { + char *str; + int keycode; + int lloc; + char *sep; + char *modificator; + } val; + +#line 84 "sqlparse.h" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + +/* Location type. */ +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE YYLTYPE; +struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +}; +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + +extern YYSTYPE orafce_sql_yylval; +extern YYLTYPE orafce_sql_yylloc; +int orafce_sql_yyparse (List **result); + +#endif /* !YY_ORAFCE_SQL_YY_SQLPARSE_H_INCLUDED */ diff --git a/src/postgres/third-party-extensions/orafce/sqlparse.y b/src/postgres/third-party-extensions/orafce/sqlparse.y new file mode 100644 index 000000000000..52e6f3740927 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/sqlparse.y @@ -0,0 +1,114 @@ +/* + * %define api.prefix {orafce_sql_yy} is not compileable on old bison 2.4 + * so I am using obsolete but still working option. + */ + +%name-prefix "orafce_sql_yy" + +%{ + +#define YYDEBUG 1 + +#define YYLLOC_DEFAULT(Current, Rhs, N) \ +do { \ +if (N) \ +(Current) = (Rhs)[1]; \ +else \ +(Current) = (Rhs)[0]; \ +} while (0) + +#include "postgres.h" +#include "orafce.h" +#include "plvlex.h" +#include "nodes/pg_list.h" + + +#define MOVE_TO_S(src,dest,col) dest->col = src.col ? pstrdup(src.col) : NULL +#define MOVE_TO(src,dest,col) dest->col = src.col + +#define FILL_NODE(src,dest) \ + MOVE_TO_S(src,dest,str), \ + MOVE_TO(src,dest,keycode), \ + MOVE_TO(src,dest,lloc), \ + MOVE_TO_S(src,dest,sep), \ + MOVE_TO(src,dest,modificator) + +static orafce_lexnode *__node; + +#define CREATE_NODE(src,type) \ + ( \ + __node = (orafce_lexnode*) palloc(sizeof(orafce_lexnode)), \ + __node->typenode = X_##type, \ + __node->classname = #type, \ + FILL_NODE(src,__node), \ + __node) + + +extern int yylex(void); /* defined as fdate_yylex in fdatescan.l */ + +static char *scanbuf; +static int scanbuflen; + +void orafce_sql_yyerror(List **result, const char *message); + +#define YYMALLOC malloc /* XXX: should use palloc? */ +#define YYFREE free /* XXX: should use pfree? */ + +%} + +%locations +%parse-param {List **result} + +%union +{ + int ival; + orafce_lexnode *node; + List *list; + struct + { + char *str; + int keycode; + int lloc; + char *sep; + char *modificator; + } val; +} + +/* BISON Declarations */ +%token X_IDENT X_NCONST X_SCONST X_OP X_PARAM X_COMMENT X_WHITESPACE X_KEYWORD X_OTHERS X_TYPECAST + +%type elements +%type anyelement +%type root + +%start root + + +/* Grammar follows */ +%% + +root: + elements { *((void**)result) = $1; } + ; + +elements: + anyelement { $$ = list_make1($1);} + | elements anyelement { $$ = lappend($1, $2);} + ; + +anyelement: + X_IDENT { $$ = (orafce_lexnode*) CREATE_NODE($1, IDENT); } + | X_NCONST { $$ = (orafce_lexnode*) CREATE_NODE($1, NCONST); } + | X_SCONST { $$ = (orafce_lexnode*) CREATE_NODE($1, SCONST); } + | X_OP { $$ = (orafce_lexnode*) CREATE_NODE($1, OP); } + | X_PARAM { $$ = (orafce_lexnode*) CREATE_NODE($1, PARAM); } + | X_COMMENT { $$ = (orafce_lexnode*) CREATE_NODE($1, COMMENT); } + | X_WHITESPACE { $$ = (orafce_lexnode*) CREATE_NODE($1, WHITESPACE); } + | X_KEYWORD { $$ = (orafce_lexnode*) CREATE_NODE($1, KEYWORD); } + | X_OTHERS { $$ = (orafce_lexnode*) CREATE_NODE($1, OTHERS); } + ; +%% + +#undef YYLTYPE + +#include "sqlscan.c" diff --git a/src/postgres/third-party-extensions/orafce/sqlscan.c b/src/postgres/third-party-extensions/orafce/sqlscan.c new file mode 100644 index 000000000000..4483e3eb3612 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/sqlscan.c @@ -0,0 +1,3327 @@ +#line 1 "sqlscan.c" + +#line 3 "sqlscan.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define yy_create_buffer orafce_sql_yy_create_buffer +#define yy_delete_buffer orafce_sql_yy_delete_buffer +#define yy_scan_buffer orafce_sql_yy_scan_buffer +#define yy_scan_string orafce_sql_yy_scan_string +#define yy_scan_bytes orafce_sql_yy_scan_bytes +#define yy_init_buffer orafce_sql_yy_init_buffer +#define yy_flush_buffer orafce_sql_yy_flush_buffer +#define yy_load_buffer_state orafce_sql_yy_load_buffer_state +#define yy_switch_to_buffer orafce_sql_yy_switch_to_buffer +#define yypush_buffer_state orafce_sql_yypush_buffer_state +#define yypop_buffer_state orafce_sql_yypop_buffer_state +#define yyensure_buffer_stack orafce_sql_yyensure_buffer_stack +#define yy_flex_debug orafce_sql_yy_flex_debug +#define yyin orafce_sql_yyin +#define yyleng orafce_sql_yyleng +#define yylex orafce_sql_yylex +#define yylineno orafce_sql_yylineno +#define yyout orafce_sql_yyout +#define yyrestart orafce_sql_yyrestart +#define yytext orafce_sql_yytext +#define yywrap orafce_sql_yywrap +#define yyalloc orafce_sql_yyalloc +#define yyrealloc orafce_sql_yyrealloc +#define yyfree orafce_sql_yyfree + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define orafce_sql_yy_create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer orafce_sql_yy_create_buffer +#endif + +#ifdef yy_delete_buffer +#define orafce_sql_yy_delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer orafce_sql_yy_delete_buffer +#endif + +#ifdef yy_scan_buffer +#define orafce_sql_yy_scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer orafce_sql_yy_scan_buffer +#endif + +#ifdef yy_scan_string +#define orafce_sql_yy_scan_string_ALREADY_DEFINED +#else +#define yy_scan_string orafce_sql_yy_scan_string +#endif + +#ifdef yy_scan_bytes +#define orafce_sql_yy_scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes orafce_sql_yy_scan_bytes +#endif + +#ifdef yy_init_buffer +#define orafce_sql_yy_init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer orafce_sql_yy_init_buffer +#endif + +#ifdef yy_flush_buffer +#define orafce_sql_yy_flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer orafce_sql_yy_flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define orafce_sql_yy_load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state orafce_sql_yy_load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define orafce_sql_yy_switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer orafce_sql_yy_switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define orafce_sql_yypush_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state orafce_sql_yypush_buffer_state +#endif + +#ifdef yypop_buffer_state +#define orafce_sql_yypop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state orafce_sql_yypop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define orafce_sql_yyensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack orafce_sql_yyensure_buffer_stack +#endif + +#ifdef yylex +#define orafce_sql_yylex_ALREADY_DEFINED +#else +#define yylex orafce_sql_yylex +#endif + +#ifdef yyrestart +#define orafce_sql_yyrestart_ALREADY_DEFINED +#else +#define yyrestart orafce_sql_yyrestart +#endif + +#ifdef yylex_init +#define orafce_sql_yylex_init_ALREADY_DEFINED +#else +#define yylex_init orafce_sql_yylex_init +#endif + +#ifdef yylex_init_extra +#define orafce_sql_yylex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra orafce_sql_yylex_init_extra +#endif + +#ifdef yylex_destroy +#define orafce_sql_yylex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy orafce_sql_yylex_destroy +#endif + +#ifdef yyget_debug +#define orafce_sql_yyget_debug_ALREADY_DEFINED +#else +#define yyget_debug orafce_sql_yyget_debug +#endif + +#ifdef yyset_debug +#define orafce_sql_yyset_debug_ALREADY_DEFINED +#else +#define yyset_debug orafce_sql_yyset_debug +#endif + +#ifdef yyget_extra +#define orafce_sql_yyget_extra_ALREADY_DEFINED +#else +#define yyget_extra orafce_sql_yyget_extra +#endif + +#ifdef yyset_extra +#define orafce_sql_yyset_extra_ALREADY_DEFINED +#else +#define yyset_extra orafce_sql_yyset_extra +#endif + +#ifdef yyget_in +#define orafce_sql_yyget_in_ALREADY_DEFINED +#else +#define yyget_in orafce_sql_yyget_in +#endif + +#ifdef yyset_in +#define orafce_sql_yyset_in_ALREADY_DEFINED +#else +#define yyset_in orafce_sql_yyset_in +#endif + +#ifdef yyget_out +#define orafce_sql_yyget_out_ALREADY_DEFINED +#else +#define yyget_out orafce_sql_yyget_out +#endif + +#ifdef yyset_out +#define orafce_sql_yyset_out_ALREADY_DEFINED +#else +#define yyset_out orafce_sql_yyset_out +#endif + +#ifdef yyget_leng +#define orafce_sql_yyget_leng_ALREADY_DEFINED +#else +#define yyget_leng orafce_sql_yyget_leng +#endif + +#ifdef yyget_text +#define orafce_sql_yyget_text_ALREADY_DEFINED +#else +#define yyget_text orafce_sql_yyget_text +#endif + +#ifdef yyget_lineno +#define orafce_sql_yyget_lineno_ALREADY_DEFINED +#else +#define yyget_lineno orafce_sql_yyget_lineno +#endif + +#ifdef yyset_lineno +#define orafce_sql_yyset_lineno_ALREADY_DEFINED +#else +#define yyset_lineno orafce_sql_yyset_lineno +#endif + +#ifdef yywrap +#define orafce_sql_yywrap_ALREADY_DEFINED +#else +#define yywrap orafce_sql_yywrap +#endif + +#ifdef yyalloc +#define orafce_sql_yyalloc_ALREADY_DEFINED +#else +#define yyalloc orafce_sql_yyalloc +#endif + +#ifdef yyrealloc +#define orafce_sql_yyrealloc_ALREADY_DEFINED +#else +#define yyrealloc orafce_sql_yyrealloc +#endif + +#ifdef yyfree +#define orafce_sql_yyfree_ALREADY_DEFINED +#else +#define yyfree orafce_sql_yyfree +#endif + +#ifdef yytext +#define orafce_sql_yytext_ALREADY_DEFINED +#else +#define yytext orafce_sql_yytext +#endif + +#ifdef yyleng +#define orafce_sql_yyleng_ALREADY_DEFINED +#else +#define yyleng orafce_sql_yyleng +#endif + +#ifdef yyin +#define orafce_sql_yyin_ALREADY_DEFINED +#else +#define yyin orafce_sql_yyin +#endif + +#ifdef yyout +#define orafce_sql_yyout_ALREADY_DEFINED +#else +#define yyout orafce_sql_yyout +#endif + +#ifdef yy_flex_debug +#define orafce_sql_yy_flex_debug_ALREADY_DEFINED +#else +#define yy_flex_debug orafce_sql_yy_flex_debug +#endif + +#ifdef yylineno +#define orafce_sql_yylineno_ALREADY_DEFINED +#else +#define yylineno orafce_sql_yylineno +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an + * integer in range [0..255] for use as an array index. + */ +#define YY_SC_TO_UI(c) ((YY_CHAR) (c)) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN (yy_start) = 1 + 2 * +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START (((yy_start) - 1) / 2) +#define YYSTATE YY_START +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin ) +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +extern int yyleng; + +extern FILE *yyin, *yyout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + #define YY_LINENO_REWIND_TO(ptr) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = (yy_hold_char); \ + YY_RESTORE_YY_MORE_OFFSET \ + (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) +#define unput(c) yyunput( c, (yytext_ptr) ) + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* Stack of input buffers. */ +static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ +static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ +static YY_BUFFER_STATE * yy_buffer_stack = NULL; /**< Stack as an array. */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ + ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ + : NULL) +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] + +/* yy_hold_char holds the character lost when yytext is formed. */ +static char yy_hold_char; +static int yy_n_chars; /* number of characters read into yy_ch_buf */ +int yyleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = NULL; +static int yy_init = 0; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void yyrestart ( FILE *input_file ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size ); +void yy_delete_buffer ( YY_BUFFER_STATE b ); +void yy_flush_buffer ( YY_BUFFER_STATE b ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer ); +void yypop_buffer_state ( void ); + +static void yyensure_buffer_stack ( void ); +static void yy_load_buffer_state ( void ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file ); +#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER ) + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len ); + +void *yyalloc ( yy_size_t ); +void *yyrealloc ( void *, yy_size_t ); +void yyfree ( void * ); + +#define yy_new_buffer yy_create_buffer +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +#define orafce_sql_yywrap() (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP +typedef flex_uint8_t YY_CHAR; + +FILE *yyin = NULL, *yyout = NULL; + +typedef int yy_state_type; + +extern int yylineno; +int yylineno = 1; + +extern char *yytext; +#ifdef yytext_ptr +#undef yytext_ptr +#endif +#define yytext_ptr yytext + +static yy_state_type yy_get_previous_state ( void ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state ); +static int yy_get_next_buffer ( void ); +static void yynoreturn yy_fatal_error ( const char* msg ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + (yytext_ptr) = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + (yy_hold_char) = *yy_cp; \ + *yy_cp = '\0'; \ + (yy_c_buf_p) = yy_cp; +#define YY_NUM_RULES 53 +#define YY_END_OF_BUFFER 54 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static const flex_int16_t yy_accept[155] = + { 0, + 0, 0, 13, 13, 0, 0, 0, 0, 12, 12, + 0, 0, 0, 0, 0, 0, 54, 52, 1, 1, + 44, 38, 52, 43, 20, 43, 43, 43, 43, 46, + 43, 51, 51, 51, 51, 51, 13, 10, 6, 6, + 7, 7, 41, 39, 12, 17, 26, 26, 22, 31, + 25, 22, 35, 35, 37, 1, 44, 32, 45, 33, + 2, 47, 3, 47, 46, 49, 42, 51, 9, 21, + 19, 16, 13, 10, 10, 11, 6, 8, 5, 4, + 41, 40, 12, 17, 17, 18, 26, 22, 22, 24, + 23, 27, 28, 27, 25, 35, 34, 36, 33, 2, + + 2, 3, 47, 50, 48, 10, 15, 11, 0, 4, + 17, 14, 18, 0, 22, 30, 23, 0, 28, 29, + 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 28, 29, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 + } ; + +static const YY_CHAR yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 2, 4, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 5, 6, 5, 7, 8, 5, 9, 10, + 10, 11, 12, 10, 13, 14, 15, 16, 16, 16, + 16, 16, 16, 16, 16, 17, 17, 18, 10, 8, + 8, 8, 5, 5, 19, 20, 19, 19, 21, 19, + 22, 22, 22, 22, 22, 22, 22, 23, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 24, 22, 22, + 10, 25, 10, 8, 22, 5, 19, 20, 19, 19, + + 21, 19, 22, 22, 22, 22, 22, 22, 22, 23, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 26, + 22, 22, 1, 5, 1, 5, 1, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22 + } ; + +static const YY_CHAR yy_meta[27] = + { 0, + 1, 2, 3, 3, 4, 5, 6, 4, 7, 1, + 8, 4, 9, 1, 8, 10, 10, 1, 11, 11, + 11, 12, 12, 12, 13, 12 + } ; + +static const flex_int16_t yy_base[193] = + { 0, + 0, 0, 201, 200, 22, 33, 202, 201, 197, 196, + 33, 40, 195, 190, 25, 44, 198, 711, 50, 53, + 0, 711, 43, 0, 711, 711, 184, 23, 185, 52, + 177, 0, 185, 184, 183, 178, 0, 72, 0, 0, + 52, 175, 0, 179, 0, 84, 0, 0, 96, 45, + 0, 0, 0, 0, 177, 75, 0, 711, 64, 176, + 105, 73, 0, 75, 0, 109, 711, 0, 711, 711, + 711, 711, 0, 0, 93, 169, 0, 92, 711, 0, + 0, 711, 0, 0, 95, 116, 0, 110, 102, 711, + 101, 711, 96, 0, 0, 0, 711, 94, 88, 0, + + 122, 0, 107, 66, 115, 127, 711, 80, 139, 0, + 135, 711, 71, 151, 136, 711, 59, 163, 54, 0, + 57, 135, 175, 187, 137, 199, 153, 211, 223, 138, + 235, 155, 247, 259, 159, 271, 711, 711, 157, 160, + 0, 49, 283, 159, 161, 0, 18, 295, 177, 162, + 0, 16, 307, 711, 320, 333, 346, 359, 372, 385, + 398, 408, 412, 419, 431, 444, 457, 470, 483, 495, + 508, 521, 529, 536, 548, 558, 566, 572, 580, 588, + 588, 594, 606, 619, 632, 636, 647, 659, 668, 680, + 689, 701 + + } ; + +static const flex_int16_t yy_def[193] = + { 0, + 154, 1, 155, 155, 156, 156, 157, 157, 158, 158, + 159, 159, 160, 160, 161, 161, 154, 154, 154, 154, + 162, 154, 163, 162, 154, 154, 162, 154, 162, 154, + 154, 164, 164, 164, 164, 164, 165, 154, 166, 166, + 154, 154, 167, 154, 168, 154, 169, 169, 154, 170, + 171, 49, 172, 172, 173, 154, 162, 154, 154, 174, + 175, 154, 176, 154, 30, 154, 154, 164, 154, 154, + 154, 154, 165, 38, 177, 154, 166, 154, 154, 178, + 167, 154, 168, 46, 179, 154, 169, 49, 180, 154, + 154, 154, 154, 181, 171, 172, 154, 182, 174, 175, + + 175, 176, 154, 154, 154, 177, 154, 154, 183, 178, + 179, 154, 154, 184, 180, 154, 154, 185, 154, 186, + 182, 187, 183, 183, 188, 183, 189, 184, 184, 190, + 184, 191, 185, 185, 192, 185, 154, 154, 187, 188, + 140, 154, 183, 189, 190, 145, 154, 184, 191, 192, + 150, 154, 185, 0, 154, 154, 154, 154, 154, 154, + 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 154 + + } ; + +static const flex_int16_t yy_nxt[738] = + { 0, + 18, 19, 20, 19, 21, 22, 23, 24, 25, 26, + 24, 24, 27, 28, 29, 30, 30, 31, 32, 33, + 34, 32, 35, 36, 18, 36, 40, 54, 132, 40, + 127, 55, 41, 40, 40, 48, 42, 40, 62, 62, + 40, 49, 48, 41, 40, 40, 54, 42, 49, 58, + 55, 56, 56, 56, 56, 56, 56, 50, 59, 59, + 93, 122, 78, 97, 50, 64, 79, 65, 65, 137, + 94, 132, 66, 74, 75, 75, 56, 56, 56, 59, + 59, 105, 105, 127, 76, 84, 85, 85, 62, 62, + 103, 103, 122, 66, 58, 66, 86, 88, 89, 89, + + 97, 107, 78, 112, 90, 108, 79, 113, 91, 101, + 116, 119, 101, 118, 117, 101, 101, 101, 154, 101, + 104, 104, 103, 103, 105, 105, 101, 66, 114, 101, + 105, 105, 101, 101, 101, 107, 101, 140, 140, 108, + 124, 125, 125, 112, 116, 107, 112, 113, 117, 142, + 147, 126, 129, 130, 130, 145, 145, 150, 150, 140, + 140, 145, 145, 131, 134, 135, 135, 116, 107, 112, + 116, 152, 142, 147, 152, 136, 124, 125, 125, 150, + 150, 109, 58, 97, 82, 80, 72, 126, 124, 125, + 125, 71, 70, 69, 67, 63, 61, 154, 52, 126, + + 124, 125, 125, 52, 46, 46, 44, 44, 38, 38, + 154, 143, 129, 130, 130, 154, 154, 154, 154, 154, + 154, 154, 154, 131, 129, 130, 130, 154, 154, 154, + 154, 154, 154, 154, 154, 131, 129, 130, 130, 154, + 154, 154, 154, 154, 154, 154, 154, 148, 134, 135, + 135, 154, 154, 154, 154, 154, 154, 154, 154, 136, + 134, 135, 135, 154, 154, 154, 154, 154, 154, 154, + 154, 136, 134, 135, 135, 154, 154, 154, 154, 154, + 154, 154, 154, 153, 124, 125, 125, 154, 154, 154, + 154, 154, 154, 154, 154, 143, 129, 130, 130, 154, + + 154, 154, 154, 154, 154, 154, 154, 148, 134, 135, + 135, 154, 154, 154, 154, 154, 154, 154, 154, 153, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 53, 53, + + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 57, 154, 154, 154, 57, 57, 60, 154, 154, + 154, 60, 60, 60, 68, 154, 154, 154, 68, 68, + 68, 73, 73, 73, 73, 73, 73, 154, 73, 73, + 73, 73, 73, 73, 77, 77, 77, 77, 77, 77, + 77, 154, 77, 77, 77, 77, 77, 81, 81, 81, + 81, 154, 81, 81, 81, 81, 81, 81, 81, 81, + 83, 83, 83, 83, 83, 83, 154, 83, 83, 83, + 83, 83, 83, 87, 87, 87, 87, 87, 87, 154, + 87, 87, 87, 87, 87, 92, 92, 92, 92, 92, + + 92, 92, 92, 92, 92, 92, 92, 92, 95, 95, + 95, 95, 95, 95, 154, 95, 95, 95, 95, 95, + 95, 96, 96, 96, 96, 96, 154, 96, 96, 96, + 96, 96, 96, 96, 98, 154, 154, 154, 154, 98, + 98, 99, 154, 154, 154, 99, 99, 99, 100, 100, + 154, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 102, 154, 154, 154, 102, 102, 106, 106, 154, + 154, 154, 106, 154, 106, 110, 154, 154, 154, 110, + 110, 111, 111, 154, 154, 154, 111, 154, 111, 115, + 115, 154, 154, 154, 115, 154, 115, 120, 120, 121, + + 154, 154, 154, 121, 121, 121, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 128, + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 133, 133, 133, 133, 133, 133, 133, 133, + 133, 133, 133, 133, 133, 138, 138, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 141, 141, 154, 154, 154, 141, 154, 141, 144, 144, + 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, + 144, 146, 146, 154, 154, 154, 146, 154, 146, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + + 149, 149, 151, 151, 154, 154, 154, 151, 154, 151, + 17, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 154, 154, 154, 154, 154, 154 + } ; + +static const flex_int16_t yy_chk[738] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 5, 15, 152, 5, + 147, 15, 5, 5, 5, 11, 5, 6, 28, 28, + 6, 11, 12, 6, 6, 6, 16, 6, 12, 23, + 16, 19, 19, 19, 20, 20, 20, 11, 23, 23, + 50, 142, 41, 121, 12, 30, 41, 30, 30, 119, + 50, 117, 30, 38, 38, 38, 56, 56, 56, 59, + 59, 104, 104, 113, 38, 46, 46, 46, 62, 62, + 64, 64, 108, 62, 99, 64, 46, 49, 49, 49, + + 98, 75, 78, 85, 49, 75, 78, 85, 49, 61, + 89, 93, 61, 91, 89, 61, 61, 61, 88, 61, + 66, 66, 103, 103, 66, 66, 101, 103, 86, 101, + 105, 105, 101, 101, 101, 106, 101, 122, 122, 106, + 109, 109, 109, 111, 115, 125, 130, 111, 115, 125, + 130, 109, 114, 114, 114, 127, 127, 132, 132, 139, + 139, 144, 144, 114, 118, 118, 118, 135, 140, 145, + 150, 135, 140, 145, 150, 118, 123, 123, 123, 149, + 149, 76, 60, 55, 44, 42, 36, 123, 124, 124, + 124, 35, 34, 33, 31, 29, 27, 17, 14, 124, + + 126, 126, 126, 13, 10, 9, 8, 7, 4, 3, + 0, 126, 128, 128, 128, 0, 0, 0, 0, 0, + 0, 0, 0, 128, 129, 129, 129, 0, 0, 0, + 0, 0, 0, 0, 0, 129, 131, 131, 131, 0, + 0, 0, 0, 0, 0, 0, 0, 131, 133, 133, + 133, 0, 0, 0, 0, 0, 0, 0, 0, 133, + 134, 134, 134, 0, 0, 0, 0, 0, 0, 0, + 0, 134, 136, 136, 136, 0, 0, 0, 0, 0, + 0, 0, 0, 136, 143, 143, 143, 0, 0, 0, + 0, 0, 0, 0, 0, 143, 148, 148, 148, 0, + + 0, 0, 0, 0, 0, 0, 0, 148, 153, 153, + 153, 0, 0, 0, 0, 0, 0, 0, 0, 153, + 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, + 155, 155, 155, 156, 156, 156, 156, 156, 156, 156, + 156, 156, 156, 156, 156, 156, 157, 157, 157, 157, + 157, 157, 157, 157, 157, 157, 157, 157, 157, 158, + 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, + 158, 158, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 160, 160, 160, 160, 160, + 160, 160, 160, 160, 160, 160, 160, 160, 161, 161, + + 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, + 161, 162, 0, 0, 0, 162, 162, 163, 0, 0, + 0, 163, 163, 163, 164, 0, 0, 0, 164, 164, + 164, 165, 165, 165, 165, 165, 165, 0, 165, 165, + 165, 165, 165, 165, 166, 166, 166, 166, 166, 166, + 166, 0, 166, 166, 166, 166, 166, 167, 167, 167, + 167, 0, 167, 167, 167, 167, 167, 167, 167, 167, + 168, 168, 168, 168, 168, 168, 0, 168, 168, 168, + 168, 168, 168, 169, 169, 169, 169, 169, 169, 0, + 169, 169, 169, 169, 169, 170, 170, 170, 170, 170, + + 170, 170, 170, 170, 170, 170, 170, 170, 171, 171, + 171, 171, 171, 171, 0, 171, 171, 171, 171, 171, + 171, 172, 172, 172, 172, 172, 0, 172, 172, 172, + 172, 172, 172, 172, 173, 0, 0, 0, 0, 173, + 173, 174, 0, 0, 0, 174, 174, 174, 175, 175, + 0, 175, 175, 175, 175, 175, 175, 175, 175, 175, + 175, 176, 0, 0, 0, 176, 176, 177, 177, 0, + 0, 0, 177, 0, 177, 178, 0, 0, 0, 178, + 178, 179, 179, 0, 0, 0, 179, 0, 179, 180, + 180, 0, 0, 0, 180, 0, 180, 181, 181, 182, + + 0, 0, 0, 182, 182, 182, 183, 183, 183, 183, + 183, 183, 183, 183, 183, 183, 183, 183, 183, 184, + 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, + 184, 184, 185, 185, 185, 185, 185, 185, 185, 185, + 185, 185, 185, 185, 185, 186, 186, 187, 187, 187, + 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, + 188, 188, 0, 0, 0, 188, 0, 188, 189, 189, + 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, + 189, 190, 190, 0, 0, 0, 190, 0, 190, 191, + 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, + + 191, 191, 192, 192, 0, 0, 0, 192, 0, 192, + 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 154, 154, 154, 154, 154, 154 + } ; + +static yy_state_type yy_last_accepting_state; +static char *yy_last_accepting_cpos; + +extern int yy_flex_debug; +int yy_flex_debug = 0; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *yytext; +#line 1 "sqlscan.l" +#line 2 "sqlscan.l" +/* +** A scanner for EMP-style numeric ranges +*/ + +#include "postgres.h" + +#include "parser/gramparse.h" +/* Not needed now that this file is compiled as part of gram.y */ +/* #include "parser/parse.h" */ +#include "parser/scansup.h" +#include "mb/pg_wchar.h" + +#include "parse_keyword.h" + +/* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */ +#undef fprintf +#define fprintf(file, fmt, msg) fprintf_to_ereport(fmt, msg) + +static void +fprintf_to_ereport(const char *fmt, const char *msg) +{ + ereport(ERROR, (errmsg_internal("%s", msg))); +} + +static int xcdepth = 0; /* depth of nesting in slash-star comments */ +static char *dolqstart; /* current $foo$ quote start string */ +static bool extended_string = false; + + +/* No reason to constrain amount of data slurped */ +#define YY_READ_BUF_SIZE 16777216 + +/* Handles to the buffer that the lexer uses internally */ + + +static YY_BUFFER_STATE scanbufhandle; + +#define SET_YYLLOC() (orafce_sql_yylval.val.lloc = yytext - scanbuf) + +/* Handles to the buffer that the lexer uses internally */ +static char *scanbuf; + +/* flex 2.5.4 doesn't bother with a decl for this */ + +int orafce_sql_yylex(void); + +void orafce_sql_scanner_init(const char *str); +void orafce_sql_scanner_finish(void); + +/* + * literalbuf is used to accumulate literal values when multiple rules + * are needed to parse a single literal. Call startlit to reset buffer + * to empty, addlit to add text. Note that the buffer is palloc'd and + * starts life afresh on every parse cycle. + */ +static char *literalbuf; /* expandable buffer */ +static int literallen; /* actual current length */ +static int literalalloc; /* current allocated buffer size */ + +#define startlit() (literalbuf[0] = '\0', literallen = 0) +static void addlit(char *ytext, int yleng); +static void addlitchar(unsigned char ychar); +static char *litbufdup(void); + +static int lexer_errposition(void); + +/* + * Each call to yylex must set yylloc to the location of the found token + * (expressed as a byte offset from the start of the input text). + * When we parse a token that requires multiple lexer rules to process, + * this should be done in the first such rule, else yylloc will point + * into the middle of the token. + */ + +/* Handles to the buffer that the lexer uses internally */ +static char *scanbuf; + +static unsigned char unescape_single_char(unsigned char c); + +#ifndef _pg_mbstrlen_with_len +#define _pg_mbstrlen_with_len(buf,loc) pg_mbstrlen_with_len(buf,loc) +#endif + +#line 1007 "sqlscan.c" +#define YY_NO_INPUT 1 +/* + * OK, here is a short description of lex/flex rules behavior. + * The longest pattern which matches an input string is always chosen. + * For equal-length patterns, the first occurring in the rules list is chosen. + * INITIAL is the starting state, to which all non-conditional rules apply. + * Exclusive states change parsing rules while the state is active. When in + * an exclusive state, only those rules defined for that state apply. + * + * We use exclusive states for quoted strings, extended comments, + * and to eliminate parsing troubles for numeric strings. + * Exclusive states: + * bit string literal + * extended C-style comments + * delimited identifiers (double-quoted identifiers) + * hexadecimal numeric string + * standard quoted strings + * extended quoted strings (support backslash escape sequences) + * $foo$ quoted strings + */ + +/* + * In order to make the world safe for Windows and Mac clients as well as + * Unix ones, we accept either \n or \r as a newline. A DOS-style \r\n + * sequence will be seen as two successive newlines, but that doesn't cause + * any problems. Comments that start with -- and extend to the next + * newline are treated as equivalent to a single whitespace character. + * + * NOTE a fine point: if there is no newline following --, we will absorb + * everything to the end of the input as a comment. This is correct. Older + * versions of Postgres failed to recognize -- as a comment if the input + * did not end with a newline. + * + * XXX perhaps \f (formfeed) should be treated as a newline as well? + * + * XXX if you change the set of whitespace characters, fix scanner_isspace() + * to agree, and see also the plpgsql lexer. + */ +/* + * SQL requires at least one newline in the whitespace separating + * string literals that are to be concatenated. Silly, but who are we + * to argue? Note that {whitespace_with_newline} should not have * after + * it, whereas {whitespace} should generally have a * after it... + */ +/* + * To ensure that {quotecontinue} can be scanned without having to back up + * if the full pattern isn't matched, we include trailing whitespace in + * {quotestop}. This matches all cases where {quotecontinue} fails to match, + * except for {quote} followed by whitespace and just one "-" (not two, + * which would start a {comment}). To cover that we have {quotefail}. + * The actions for {quotestop} and {quotefail} must throw back characters + * beyond the quote proper. + */ +/* Bit string + * It is tempting to scan the string for only those characters + * which are allowed. However, this leads to silently swallowed + * characters if illegal characters are included in the string. + * For example, if xbinside is [01] then B'ABCD' is interpreted + * as a zero-length string, and the ABCD' is lost! + * Better to pass the string forward and let the input routines + * validate the contents. + */ +/* Hexadecimal number */ +/* National character */ +/* Quoted string that allows backslash escapes */ +/* Extended quote + * xqdouble implements embedded quote, '''' + */ +/* $foo$ style quotes ("dollar quoting") + * The quoted string starts with $foo$ where "foo" is an optional string + * in the form of an identifier, except that it may not contain "$", + * and extends to the first occurrence of an identical string. + * There is *no* processing of the quoted text. + * + * {dolqfailed} is an error rule to avoid scanner backup when {dolqdelim} + * fails to match its trailing "$". + */ +/* Double quote + * Allows embedded spaces and other special characters into identifiers. + */ +/* C-style comments + * + * The "extended comment" syntax closely resembles allowable operator syntax. + * The tricky part here is to get lex to recognize a string starting with + * slash-star as a comment, when interpreting it as an operator would produce + * a longer match --- remember lex will prefer a longer match! Also, if we + * have something like plus-slash-star, lex will think this is a 3-character + * operator whereas we want to see it as a + operator and a comment start. + * The solution is two-fold: + * 1. append {op_chars}* to xcstart so that it matches as much text as + * {operator} would. Then the tie-breaker (first matching rule of same + * length) ensures xcstart wins. We put back the extra stuff with yyless() + * in case it contains a star-slash that should terminate the comment. + * 2. In the operator rule, check for slash-star within the operator, and + * if found throw it back with yyless(). This handles the plus-slash-star + * problem. + * Dash-dash comments have similar interactions with the operator rule. + */ +/* + * "self" is the set of chars that should be returned as single-character + * tokens. "op_chars" is the set of chars that can make up "Op" tokens, + * which can be one or more characters long (but if a single-char token + * appears in the "self" set, it is not to be returned as an Op). Note + * that the sets overlap, but each has some chars that are not in the other. + * + * If you change either set, adjust the character lists appearing in the + * rule for "operator"! + */ +/* we no longer allow unary minus in numbers. + * instead we pass it separately to parser. there it gets + * coerced via doNegate() -- Leon aug 20 1999 + * + * {realfail1} and {realfail2} are added to prevent the need for scanner + * backup when the {real} rule fails to match completely. + */ +/* + * Dollar quoted strings are totally opaque, and no escaping is done on them. + * Other quoted strings must allow some special characters such as single-quote + * and newline. + * Embedded single-quotes are implemented both in the SQL standard + * style of two adjacent single quotes "''" and in the Postgres/Java style + * of escaped-quote "\'". + * Other embedded escaped characters are matched explicitly and the leading + * backslash is dropped from the string. + * Note that xcstart must appear before operator, as explained above! + * Also whitespace (comment) must appear before operator. + */ +#line 1135 "sqlscan.c" + +#define INITIAL 0 +#define xb 1 +#define xc 2 +#define xd 3 +#define xh 4 +#define xe 5 +#define xq 6 +#define xdolq 7 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +static int yy_init_globals ( void ); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy ( void ); + +int yyget_debug ( void ); + +void yyset_debug ( int debug_flag ); + +YY_EXTRA_TYPE yyget_extra ( void ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined ); + +FILE *yyget_in ( void ); + +void yyset_in ( FILE * _in_str ); + +FILE *yyget_out ( void ); + +void yyset_out ( FILE * _out_str ); + + int yyget_leng ( void ); + +char *yyget_text ( void ); + +int yyget_lineno ( void ); + +void yyset_lineno ( int _line_number ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap ( void ); +#else +extern int yywrap ( void ); +#endif +#endif + +#ifndef YY_NO_UNPUT + +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int ); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * ); +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus +static int yyinput ( void ); +#else +static int input ( void ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + int n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex (void); + +#define YY_DECL int yylex (void) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK /*LINTED*/break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + + if ( !(yy_init) ) + { + (yy_init) = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! (yy_start) ) + (yy_start) = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE ); + } + + yy_load_buffer_state( ); + } + + { +#line 308 "sqlscan.l" + + +#line 1360 "sqlscan.c" + + while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ + { + yy_cp = (yy_c_buf_p); + + /* Support of yytext. */ + *yy_cp = (yy_hold_char); + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = (yy_start); +yy_match: + do + { + YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 155 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + ++yy_cp; + } + while ( yy_current_state != 154 ); + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = (yy_hold_char); + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + goto yy_find_action; + +case 1: +/* rule 1 can match eol */ +YY_RULE_SETUP +#line 310 "sqlscan.l" +{ + SET_YYLLOC(); + yylval.val.str = yytext; + yylval.val.modificator = NULL; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_WHITESPACE; + } + YY_BREAK +case 2: +YY_RULE_SETUP +#line 319 "sqlscan.l" +{ + SET_YYLLOC(); + yylval.val.str = yytext; + yylval.val.modificator = "sc"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_COMMENT; + } + YY_BREAK +case 3: +YY_RULE_SETUP +#line 329 "sqlscan.l" +{ + /* Set location in case of syntax error in comment */ + SET_YYLLOC(); + xcdepth = 0; + BEGIN(xc); + /* Put back any characters past slash-star; see above */ + startlit(); + addlitchar('/'); + addlitchar('*'); + + yyless(2); + } + YY_BREAK +case 4: +YY_RULE_SETUP +#line 342 "sqlscan.l" +{ + xcdepth++; + /* Put back any characters past slash-star; see above */ + addlitchar('/'); + addlitchar('*'); + + yyless(2); + } + YY_BREAK +case 5: +YY_RULE_SETUP +#line 351 "sqlscan.l" +{ + if (xcdepth <= 0) + { + BEGIN(INITIAL); + addlitchar('*'); + addlitchar('/'); + + yylval.val.str = litbufdup(); + yylval.val.modificator = "ec"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_COMMENT; + } + else + { + xcdepth--; + addlitchar('*'); + addlitchar('/'); + } + + } + YY_BREAK +case 6: +/* rule 6 can match eol */ +YY_RULE_SETUP +#line 373 "sqlscan.l" +{ + addlit(yytext, yyleng); + } + YY_BREAK +case 7: +YY_RULE_SETUP +#line 377 "sqlscan.l" +{ + addlit(yytext, yyleng); + } + YY_BREAK +case 8: +YY_RULE_SETUP +#line 381 "sqlscan.l" +{ + addlit(yytext, yyleng); + } + YY_BREAK +case YY_STATE_EOF(xc): +#line 385 "sqlscan.l" +{ + yylval.val.str = litbufdup(); + yylval.val.modificator = "ecu"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_COMMENT; + + } + YY_BREAK +case 9: +YY_RULE_SETUP +#line 394 "sqlscan.l" +{ + /* Binary bit type. + * At some point we should simply pass the string + * forward to the parser and label it there. + * In the meantime, place a leading "b" on the string + * to mark it for the input routine as a binary string. + */ + SET_YYLLOC(); + BEGIN(xb); + startlit(); + addlitchar('b'); + } + YY_BREAK +case 10: +/* rule 10 can match eol */ +#line 407 "sqlscan.l" +case 11: +/* rule 11 can match eol */ +YY_RULE_SETUP +#line 407 "sqlscan.l" +{ + yyless(1); + BEGIN(INITIAL); + yylval.val.str = litbufdup(); + yylval.val.modificator = "b"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_NCONST; + } + YY_BREAK +case 12: +/* rule 12 can match eol */ +#line 417 "sqlscan.l" +case 13: +/* rule 13 can match eol */ +YY_RULE_SETUP +#line 417 "sqlscan.l" +{ + addlit(yytext, yyleng); + } + YY_BREAK +case 14: +/* rule 14 can match eol */ +#line 421 "sqlscan.l" +case 15: +/* rule 15 can match eol */ +YY_RULE_SETUP +#line 421 "sqlscan.l" +{ + /* ignore */ + } + YY_BREAK +case YY_STATE_EOF(xb): +#line 424 "sqlscan.l" +{ + yylval.val.str = litbufdup(); + yylval.val.modificator = "bu"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_NCONST; + } + YY_BREAK +case 16: +YY_RULE_SETUP +#line 432 "sqlscan.l" +{ + /* Hexadecimal bit type. + * At some point we should simply pass the string + * forward to the parser and label it there. + * In the meantime, place a leading "x" on the string + * to mark it for the input routine as a hex string. + */ + SET_YYLLOC(); + BEGIN(xh); + startlit(); + addlitchar('x'); + } + YY_BREAK +case 17: +/* rule 17 can match eol */ +#line 445 "sqlscan.l" +case 18: +/* rule 18 can match eol */ +YY_RULE_SETUP +#line 445 "sqlscan.l" +{ + yyless(1); + BEGIN(INITIAL); + yylval.val.str = litbufdup(); + yylval.val.modificator = "x"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_NCONST; + } + YY_BREAK +case YY_STATE_EOF(xh): +#line 454 "sqlscan.l" +{ + yylval.val.str = litbufdup(); + yylval.val.modificator = "xu"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_NCONST; + } + YY_BREAK +case 19: +YY_RULE_SETUP +#line 462 "sqlscan.l" +{ + /* National character. + * We will pass this along as a normal character string, + * but preceded with an internally-generated "NCHAR". + */ + const char *keyword; + int keycode; + + SET_YYLLOC(); + yyless(1); /* eat only 'n' this time */ + /* nchar had better be a keyword! */ + keyword = orafce_scan_keyword("nchar", &keycode); + Assert(keyword != NULL); + yylval.val.str = (char*) keyword; + yylval.val.keycode = keycode; + yylval.val.modificator = NULL; + yylval.val.sep = NULL; + return X_KEYWORD; + } + YY_BREAK +case 20: +YY_RULE_SETUP +#line 482 "sqlscan.l" +{ + SET_YYLLOC(); + BEGIN(xq); + extended_string = false; + startlit(); + } + YY_BREAK +case 21: +YY_RULE_SETUP +#line 488 "sqlscan.l" +{ + SET_YYLLOC(); + BEGIN(xe); + extended_string = true; + startlit(); + } + YY_BREAK +case 22: +/* rule 22 can match eol */ +#line 495 "sqlscan.l" +case 23: +/* rule 23 can match eol */ +YY_RULE_SETUP +#line 495 "sqlscan.l" +{ + yyless(1); + BEGIN(INITIAL); + yylval.val.str = litbufdup(); + yylval.val.modificator = extended_string ? "es" : "qs"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_SCONST; + } + YY_BREAK +case 24: +YY_RULE_SETUP +#line 504 "sqlscan.l" +{ + addlitchar('\''); + } + YY_BREAK +case 25: +/* rule 25 can match eol */ +YY_RULE_SETUP +#line 507 "sqlscan.l" +{ + addlit(yytext, yyleng); + } + YY_BREAK +case 26: +/* rule 26 can match eol */ +YY_RULE_SETUP +#line 510 "sqlscan.l" +{ + addlit(yytext, yyleng); + } + YY_BREAK +case 27: +/* rule 27 can match eol */ +YY_RULE_SETUP +#line 513 "sqlscan.l" +{ + addlitchar(unescape_single_char(yytext[1])); + } + YY_BREAK +case 28: +YY_RULE_SETUP +#line 516 "sqlscan.l" +{ + unsigned char c = strtoul(yytext+1, NULL, 8); + + addlitchar(c); + } + YY_BREAK +case 29: +YY_RULE_SETUP +#line 521 "sqlscan.l" +{ + unsigned char c = strtoul(yytext+2, NULL, 16); + + addlitchar(c); + } + YY_BREAK +case 30: +/* rule 30 can match eol */ +YY_RULE_SETUP +#line 526 "sqlscan.l" +{ + /* ignore */ + } + YY_BREAK +case 31: +YY_RULE_SETUP +#line 529 "sqlscan.l" +{ + /* This is only needed for \ just before EOF */ + addlitchar(yytext[0]); + } + YY_BREAK +case YY_STATE_EOF(xq): +case YY_STATE_EOF(xe): +#line 533 "sqlscan.l" +{ + yylval.val.str = litbufdup(); + yylval.val.modificator = extended_string ? "esu" : "qsu"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_SCONST; + } + YY_BREAK +case 32: +YY_RULE_SETUP +#line 541 "sqlscan.l" +{ + SET_YYLLOC(); + dolqstart = pstrdup(yytext); + BEGIN(xdolq); + startlit(); + } + YY_BREAK +case 33: +YY_RULE_SETUP +#line 547 "sqlscan.l" +{ + /* throw back all but the initial "$" */ + yyless(1); + /* and treat it as {other} */ + yylval.val.str = yytext; + yylval.val.modificator = "dolqf"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_OTHERS; + } + YY_BREAK +case 34: +YY_RULE_SETUP +#line 557 "sqlscan.l" +{ + if (strcmp(yytext, dolqstart) == 0) + { + yylval.val.sep = dolqstart; + yylval.val.modificator = "dolq"; + BEGIN(INITIAL); + yylval.val.str = litbufdup(); + yylval.val.keycode = -1; + return X_SCONST; + } + else + { + /* + * When we fail to match $...$ to dolqstart, transfer + * the $... part to the output, but put back the final + * $ for rescanning. Consider $delim$...$junk$delim$ + */ + addlit(yytext, yyleng-1); + yyless(yyleng-1); + } + } + YY_BREAK +case 35: +/* rule 35 can match eol */ +YY_RULE_SETUP +#line 578 "sqlscan.l" +{ + addlit(yytext, yyleng); + } + YY_BREAK +case 36: +YY_RULE_SETUP +#line 581 "sqlscan.l" +{ + addlit(yytext, yyleng); + } + YY_BREAK +case 37: +YY_RULE_SETUP +#line 584 "sqlscan.l" +{ + /* This is only needed for inside the quoted text */ + addlitchar(yytext[0]); + } + YY_BREAK +case YY_STATE_EOF(xdolq): +#line 588 "sqlscan.l" +{ + yylval.val.sep = dolqstart; + yylval.val.modificator = "dolqu"; + yylval.val.str = litbufdup(); + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_SCONST; + } + YY_BREAK +case 38: +YY_RULE_SETUP +#line 597 "sqlscan.l" +{ + SET_YYLLOC(); + BEGIN(xd); + startlit(); + } + YY_BREAK +case 39: +YY_RULE_SETUP +#line 602 "sqlscan.l" +{ + char *ident; + + BEGIN(INITIAL); + if (literallen == 0) + yyerror(NULL, "zero-length delimited identifier"); + ident = litbufdup(); + if (literallen >= NAMEDATALEN) + truncate_identifier(ident, literallen, true); + yylval.val.modificator = "dq"; + yylval.val.str = ident; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_IDENT; + } + YY_BREAK +case 40: +YY_RULE_SETUP +#line 617 "sqlscan.l" +{ + addlitchar('"'); + } + YY_BREAK +case 41: +/* rule 41 can match eol */ +YY_RULE_SETUP +#line 620 "sqlscan.l" +{ + addlit(yytext, yyleng); + } + YY_BREAK +case YY_STATE_EOF(xd): +#line 623 "sqlscan.l" +{ + yylval.val.modificator = "dqu"; + yylval.val.str = litbufdup(); + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_IDENT; + } + YY_BREAK +case 42: +YY_RULE_SETUP +#line 630 "sqlscan.l" +{ + SET_YYLLOC(); + yylval.val.modificator = "typecast"; + yylval.val.keycode = X_TYPECAST; + yylval.val.sep = NULL; + return X_OTHERS; + } + YY_BREAK +case 43: +YY_RULE_SETUP +#line 638 "sqlscan.l" +{ + SET_YYLLOC(); + yylval.val.str = yytext; + yylval.val.modificator = "self"; + yylval.val.keycode = yytext[0]; + yylval.val.sep = NULL; + return X_OTHERS; + } + YY_BREAK +case 44: +YY_RULE_SETUP +#line 647 "sqlscan.l" +{ + /* + * Check for embedded slash-star or dash-dash; those + * are comment starts, so operator must stop there. + * Note that slash-star or dash-dash at the first + * character will match a prior rule, not this one. + */ + int nchars = yyleng; + char *slashstar = strstr(yytext, "/*"); + char *dashdash = strstr(yytext, "--"); + + if (slashstar && dashdash) + { + /* if both appear, take the first one */ + if (slashstar > dashdash) + slashstar = dashdash; + } + else if (!slashstar) + slashstar = dashdash; + if (slashstar) + nchars = slashstar - yytext; + + /* + * For SQL compatibility, '+' and '-' cannot be the + * last char of a multi-char operator unless the operator + * contains chars that are not in SQL operators. + * The idea is to lex '=-' as two operators, but not + * to forbid operator names like '?-' that could not be + * sequences of SQL operators. + */ + while (nchars > 1 && + (yytext[nchars-1] == '+' || + yytext[nchars-1] == '-')) + { + int ic; + + for (ic = nchars-2; ic >= 0; ic--) + { + if (strchr("~!@#^&|`?%", yytext[ic])) + break; + } + if (ic >= 0) + break; /* found a char that makes it OK */ + nchars--; /* else remove the +/-, and check again */ + } + + SET_YYLLOC(); + + if (nchars < yyleng) + { + /* Strip the unwanted chars from the token */ + yyless(nchars); + /* + * If what we have left is only one char, and it's + * one of the characters matching "self", then + * return it as a character token the same way + * that the "self" rule would have. + */ + if (nchars == 1 && + strchr(",()[].;:+-*/%^<>=", yytext[0])) + { + yylval.val.str = yytext; + yylval.val.modificator = NULL; + yylval.val.keycode = yytext[0]; + yylval.val.sep = NULL; + return X_OTHERS; + } + } + + /* + * Complain if operator is too long. Unlike the case + * for identifiers, we make this an error not a notice- + * and-truncate, because the odds are we are looking at + * a syntactic mistake anyway. + */ + if (nchars >= NAMEDATALEN) + yyerror(NULL, "operator too long"); + + /* Convert "!=" operator to "<>" for compatibility */ + yylval.val.modificator = NULL; + if (strcmp(yytext, "!=") == 0) + yylval.val.str = pstrdup("<>"); + else + yylval.val.str = pstrdup(yytext); + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_OP; + } + YY_BREAK +case 45: +YY_RULE_SETUP +#line 736 "sqlscan.l" +{ + SET_YYLLOC(); + yylval.val.modificator = NULL; + yylval.val.str = yytext; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_PARAM; + } + YY_BREAK +case 46: +YY_RULE_SETUP +#line 745 "sqlscan.l" +{ + long val; + char* endptr; + + SET_YYLLOC(); + errno = 0; + val = strtol(yytext, &endptr, 10); + if (*endptr != '\0' || errno == ERANGE +#ifdef HAVE_LONG_INT_64 + /* if long > 32 bits, check for overflow of int4 */ + || val != (long) ((int32) val) +#endif + ) + { + /* integer too large, treat it as a float */ + yylval.val.str = pstrdup(yytext); + yylval.val.modificator = "f"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_NCONST; + } + yylval.val.str = yytext; + yylval.val.modificator = "i"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_NCONST; + } + YY_BREAK +case 47: +YY_RULE_SETUP +#line 772 "sqlscan.l" +{ + SET_YYLLOC(); + yylval.val.str = pstrdup(yytext); + yylval.val.modificator = "f"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_NCONST; + } + YY_BREAK +case 48: +YY_RULE_SETUP +#line 780 "sqlscan.l" +{ + SET_YYLLOC(); + yylval.val.str = pstrdup(yytext); + yylval.val.modificator = "f"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_NCONST; + } + YY_BREAK +case 49: +YY_RULE_SETUP +#line 788 "sqlscan.l" +{ + /* + * throw back the [Ee], and treat as {decimal}. Note + * that it is possible the input is actually {integer}, + * but since this case will almost certainly lead to a + * syntax error anyway, we don't bother to distinguish. + */ + yyless(yyleng-1); + SET_YYLLOC(); + yylval.val.str = pstrdup(yytext); + yylval.val.modificator = "f"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_NCONST; + } + YY_BREAK +case 50: +YY_RULE_SETUP +#line 803 "sqlscan.l" +{ + /* throw back the [Ee][+-], and proceed as above */ + yyless(yyleng-2); + SET_YYLLOC(); + yylval.val.str = pstrdup(yytext); + yylval.val.modificator = "f"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_NCONST; + } + YY_BREAK +case 51: +YY_RULE_SETUP +#line 815 "sqlscan.l" +{ + char *ident; + const char *keyword; + int keycode; + + SET_YYLLOC(); + + /* nchar had better be a keyword! */ + keyword = orafce_scan_keyword("nchar", &keycode); + + /* Is it a keyword? */ + keyword = orafce_scan_keyword(yytext, &keycode); + if (keyword != NULL) + { + yylval.val.str = (char*) keyword; + yylval.val.keycode = keycode; + yylval.val.modificator = NULL; + yylval.val.sep = NULL; + return X_KEYWORD; + } + + /* + * No. Convert the identifier to lower case, and truncate + * if necessary. + */ + ident = downcase_truncate_identifier(yytext, yyleng, true); + yylval.val.str = ident; + yylval.val.modificator = NULL; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_IDENT; + } + YY_BREAK +case 52: +YY_RULE_SETUP +#line 848 "sqlscan.l" +{ + SET_YYLLOC(); + yylval.val.str = yytext; + yylval.val.modificator = NULL; + yylval.val.keycode = yytext[0]; + yylval.val.sep = NULL; + return X_OTHERS; + } + YY_BREAK +case YY_STATE_EOF(INITIAL): +#line 857 "sqlscan.l" +{ + SET_YYLLOC(); + yyterminate(); + } + YY_BREAK +case 53: +YY_RULE_SETUP +#line 862 "sqlscan.l" +YY_FATAL_ERROR( "flex scanner jammed" ); + YY_BREAK +#line 2179 "sqlscan.c" + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = (yy_hold_char); + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++(yy_c_buf_p); + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_END_OF_FILE: + { + (yy_did_buffer_switch_on_eof) = 0; + + if ( yywrap( ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = + (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + (yy_c_buf_p) = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of user's declarations */ +} /* end of yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (void) +{ + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = (yytext_ptr); + int number_to_move, i; + int ret_val; + + if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr) - 1); + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + + int yy_c_buf_p_offset = + (int) ((yy_c_buf_p) - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yyrealloc( (void *) b->yy_ch_buf, + (yy_size_t) (b->yy_buf_size + 2) ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = NULL; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + (yy_n_chars), num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + if ( (yy_n_chars) == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if (((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + int new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( + (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + /* "- 2" to take care of EOB's */ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); + } + + (yy_n_chars) += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + + (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (void) +{ + yy_state_type yy_current_state; + char *yy_cp; + + yy_current_state = (yy_start); + + for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + { + YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 155 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) +{ + int yy_is_jam; + char *yy_cp = (yy_c_buf_p); + + YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 155 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + yy_is_jam = (yy_current_state == 154); + + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_UNPUT + +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (void) +#else + static int input (void) +#endif + +{ + int c; + + *(yy_c_buf_p) = (yy_hold_char); + + if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + /* This was really a NUL. */ + *(yy_c_buf_p) = '\0'; + + else + { /* need more input */ + int offset = (int) ((yy_c_buf_p) - (yytext_ptr)); + ++(yy_c_buf_p); + + switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart( yyin ); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap( ) ) + return 0; + + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = (yytext_ptr) + offset; + break; + } + } + } + + c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ + *(yy_c_buf_p) = '\0'; /* preserve yytext */ + (yy_hold_char) = *++(yy_c_buf_p); + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * + * @note This function does not reset the start condition to @c INITIAL . + */ + void yyrestart (FILE * input_file ) +{ + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE ); + } + + yy_init_buffer( YY_CURRENT_BUFFER, input_file ); + yy_load_buffer_state( ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * + */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) +{ + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state( ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + (yy_did_buffer_switch_on_eof) = 1; +} + +static void yy_load_buffer_state (void) +{ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + (yy_hold_char) = *(yy_c_buf_p); +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * + * @return the allocated buffer state. + */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size ) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file ); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * + */ + void yy_delete_buffer (YY_BUFFER_STATE b ) +{ + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree( (void *) b->yy_ch_buf ); + + yyfree( (void *) b ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file ) + +{ + int oerrno = errno; + + yy_flush_buffer( b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * + */ + void yy_flush_buffer (YY_BUFFER_STATE b ) +{ + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state( ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * + */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ) +{ + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + (yy_buffer_stack_top)++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * + */ +void yypop_buffer_state (void) +{ + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + if ((yy_buffer_stack_top) > 0) + --(yy_buffer_stack_top); + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void yyensure_buffer_stack (void) +{ + yy_size_t num_to_alloc; + + if (!(yy_buffer_stack)) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ + (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + (yy_buffer_stack_max) = num_to_alloc; + (yy_buffer_stack_top) = 0; + return; + } + + if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + yy_size_t grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = (yy_buffer_stack_max) + grow_size; + (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc + ((yy_buffer_stack), + num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); + (yy_buffer_stack_max) = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size ) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return NULL; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = NULL; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer( b ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (const char * yystr ) +{ + + return yy_scan_bytes( yystr, (int) strlen(yystr) ); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len ) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc( n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer( buf, n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yynoreturn yy_fatal_error (const char* msg ) +{ + fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = (yy_hold_char); \ + (yy_c_buf_p) = yytext + yyless_macro_arg; \ + (yy_hold_char) = *(yy_c_buf_p); \ + *(yy_c_buf_p) = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the current line number. + * + */ +int yyget_lineno (void) +{ + + return yylineno; +} + +/** Get the input stream. + * + */ +FILE *yyget_in (void) +{ + return yyin; +} + +/** Get the output stream. + * + */ +FILE *yyget_out (void) +{ + return yyout; +} + +/** Get the length of the current token. + * + */ +int yyget_leng (void) +{ + return yyleng; +} + +/** Get the current token. + * + */ + +char *yyget_text (void) +{ + return yytext; +} + +/** Set the current line number. + * @param _line_number line number + * + */ +void yyset_lineno (int _line_number ) +{ + + yylineno = _line_number; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param _in_str A readable stream. + * + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * _in_str ) +{ + yyin = _in_str ; +} + +void yyset_out (FILE * _out_str ) +{ + yyout = _out_str ; +} + +int yyget_debug (void) +{ + return yy_flex_debug; +} + +void yyset_debug (int _bdebug ) +{ + yy_flex_debug = _bdebug ; +} + +static int yy_init_globals (void) +{ + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. + */ + + (yy_buffer_stack) = NULL; + (yy_buffer_stack_top) = 0; + (yy_buffer_stack_max) = 0; + (yy_c_buf_p) = NULL; + (yy_init) = 0; + (yy_start) = 0; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = NULL; + yyout = NULL; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (void) +{ + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer( YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(); + } + + /* Destroy the stack itself. */ + yyfree((yy_buffer_stack) ); + (yy_buffer_stack) = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * yylex() is called, initialization will occur. */ + yy_init_globals( ); + + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, const char * s2, int n ) +{ + + int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (const char * s ) +{ + int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size ) +{ + return malloc(size); +} + +void *yyrealloc (void * ptr, yy_size_t size ) +{ + + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return realloc(ptr, size); +} + +void yyfree (void * ptr ) +{ + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 862 "sqlscan.l" + + +/* + * lexer_errposition + * Report a lexical-analysis-time cursor position, if possible. + * + * This is expected to be used within an ereport() call. The return value + * is a dummy (always 0, in fact). + * + * Note that this can only be used for messages from the lexer itself, + * since it depends on scanbuf to still be valid. + */ +static int +lexer_errposition(void) +{ + int pos; + + /* Convert byte offset to character number */ + pos = _pg_mbstrlen_with_len(scanbuf, orafce_sql_yylval.val.lloc) + 1; + /* And pass it to the ereport mechanism */ + +#if PG_VERSION_NUM >= 130000 + + errposition(pos); + + return pos; + +#else + + return errposition(pos); + +#endif + +} + +/* + * yyerror + * Report a lexer or grammar error. + * + * The message's cursor position identifies the most recently lexed token. + * This is OK for syntax error messages from the Bison parser, because Bison + * parsers report error as soon as the first unparsable token is reached. + * Beware of using yyerror for other purposes, as the cursor position might + * be misleading! + */ +void +orafce_sql_yyerror(List **result, const char *message) +{ + const char *loc = scanbuf + orafce_sql_yylval.val.lloc; + + if (*loc == YY_END_OF_BUFFER_CHAR) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s at end of input", message), + lexer_errposition())); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s at or near \"%s\"", message, loc), + lexer_errposition())); + } +} + + +/* + * Called before any actual parsing is done + */ +void +orafce_sql_scanner_init(const char *str) +{ + Size slen = strlen(str); + + /* + * Might be left over after ereport() + */ + if (YY_CURRENT_BUFFER) + yy_delete_buffer(YY_CURRENT_BUFFER); + + /* + * Make a scan buffer with special termination needed by flex. + */ + scanbuflen = slen; + scanbuf = palloc(slen + 2); + memcpy(scanbuf, str, slen); + scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR; + scanbufhandle = yy_scan_buffer(scanbuf, slen + 2); + + /* initialize literal buffer to a reasonable but expansible size */ + literalalloc = 128; + literalbuf = (char *) palloc(literalalloc); + startlit(); + + BEGIN(INITIAL); +} + + +/* + * Called after parsing is done to clean up after fdate_scanner_init() + */ +void +orafce_sql_scanner_finish(void) +{ + yy_delete_buffer(scanbufhandle); + pfree(scanbuf); +} + +static void +addlit(char *ytext, int yleng) +{ + /* enlarge buffer if needed */ + if ((literallen+yleng) >= literalalloc) + { + do { + literalalloc *= 2; + } while ((literallen+yleng) >= literalalloc); + literalbuf = (char *) repalloc(literalbuf, literalalloc); + } + /* append new data, add trailing null */ + memcpy(literalbuf+literallen, ytext, yleng); + literallen += yleng; + literalbuf[literallen] = '\0'; +} + + +static void +addlitchar(unsigned char ychar) +{ + /* enlarge buffer if needed */ + if ((literallen+1) >= literalalloc) + { + literalalloc *= 2; + literalbuf = (char *) repalloc(literalbuf, literalalloc); + } + /* append new data, add trailing null */ + literalbuf[literallen] = ychar; + literallen += 1; + literalbuf[literallen] = '\0'; +} + + +/* + * One might be tempted to write pstrdup(literalbuf) instead of this, + * but for long literals this is much faster because the length is + * already known. + */ +static char * +litbufdup(void) +{ + char *new; + + new = palloc(literallen + 1); + memcpy(new, literalbuf, literallen+1); + return new; +} + + +static unsigned char +unescape_single_char(unsigned char c) +{ + switch (c) + { + case 'b': + return '\b'; + case 'f': + return '\f'; + case 'n': + return '\n'; + case 'r': + return '\r'; + case 't': + return '\t'; + default: + return c; + } +} + + + diff --git a/src/postgres/third-party-extensions/orafce/sqlscan.l b/src/postgres/third-party-extensions/orafce/sqlscan.l new file mode 100644 index 000000000000..919346e42ba3 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/sqlscan.l @@ -0,0 +1,1041 @@ +%{ +/* +** A scanner for EMP-style numeric ranges +*/ + +#include "postgres.h" + +#include "parser/gramparse.h" +/* Not needed now that this file is compiled as part of gram.y */ +/* #include "parser/parse.h" */ +#include "parser/scansup.h" +#include "mb/pg_wchar.h" + +#include "parse_keyword.h" + +/* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */ +#undef fprintf +#define fprintf(file, fmt, msg) fprintf_to_ereport(fmt, msg) + +static void +fprintf_to_ereport(const char *fmt, const char *msg) +{ + ereport(ERROR, (errmsg_internal("%s", msg))); +} + +static int xcdepth = 0; /* depth of nesting in slash-star comments */ +static char *dolqstart; /* current $foo$ quote start string */ +static bool extended_string = false; + + +/* No reason to constrain amount of data slurped */ +#define YY_READ_BUF_SIZE 16777216 + +/* Handles to the buffer that the lexer uses internally */ + + +static YY_BUFFER_STATE scanbufhandle; + +#define SET_YYLLOC() (orafce_sql_yylval.val.lloc = yytext - scanbuf) + +/* Handles to the buffer that the lexer uses internally */ +static char *scanbuf; + +/* flex 2.5.4 doesn't bother with a decl for this */ + +int orafce_sql_yylex(void); + +void orafce_sql_scanner_init(const char *str); +void orafce_sql_scanner_finish(void); + +/* + * literalbuf is used to accumulate literal values when multiple rules + * are needed to parse a single literal. Call startlit to reset buffer + * to empty, addlit to add text. Note that the buffer is palloc'd and + * starts life afresh on every parse cycle. + */ +static char *literalbuf; /* expandable buffer */ +static int literallen; /* actual current length */ +static int literalalloc; /* current allocated buffer size */ + +#define startlit() (literalbuf[0] = '\0', literallen = 0) +static void addlit(char *ytext, int yleng); +static void addlitchar(unsigned char ychar); +static char *litbufdup(void); + +static int lexer_errposition(void); + +/* + * Each call to yylex must set yylloc to the location of the found token + * (expressed as a byte offset from the start of the input text). + * When we parse a token that requires multiple lexer rules to process, + * this should be done in the first such rule, else yylloc will point + * into the middle of the token. + */ + +/* Handles to the buffer that the lexer uses internally */ +static char *scanbuf; + +static unsigned char unescape_single_char(unsigned char c); + +#ifndef _pg_mbstrlen_with_len +#define _pg_mbstrlen_with_len(buf,loc) pg_mbstrlen_with_len(buf,loc) +#endif + +%} + +%option 8bit +%option never-interactive +%option nodefault +%option noinput +%option nounput +%option noyywrap +%option prefix="orafce_sql_yy" + +/* + * OK, here is a short description of lex/flex rules behavior. + * The longest pattern which matches an input string is always chosen. + * For equal-length patterns, the first occurring in the rules list is chosen. + * INITIAL is the starting state, to which all non-conditional rules apply. + * Exclusive states change parsing rules while the state is active. When in + * an exclusive state, only those rules defined for that state apply. + * + * We use exclusive states for quoted strings, extended comments, + * and to eliminate parsing troubles for numeric strings. + * Exclusive states: + * bit string literal + * extended C-style comments + * delimited identifiers (double-quoted identifiers) + * hexadecimal numeric string + * standard quoted strings + * extended quoted strings (support backslash escape sequences) + * $foo$ quoted strings + */ + +%x xb +%x xc +%x xd +%x xh +%x xe +%x xq +%x xdolq + + +/* + * In order to make the world safe for Windows and Mac clients as well as + * Unix ones, we accept either \n or \r as a newline. A DOS-style \r\n + * sequence will be seen as two successive newlines, but that doesn't cause + * any problems. Comments that start with -- and extend to the next + * newline are treated as equivalent to a single whitespace character. + * + * NOTE a fine point: if there is no newline following --, we will absorb + * everything to the end of the input as a comment. This is correct. Older + * versions of Postgres failed to recognize -- as a comment if the input + * did not end with a newline. + * + * XXX perhaps \f (formfeed) should be treated as a newline as well? + * + * XXX if you change the set of whitespace characters, fix scanner_isspace() + * to agree, and see also the plpgsql lexer. + */ + +space [ \t\n\r\f] +horiz_space [ \t\f] +newline [\n\r] +non_newline [^\n\r] + +comment ("--"{non_newline}*) + +whitespace {space}+ + +/* + * SQL requires at least one newline in the whitespace separating + * string literals that are to be concatenated. Silly, but who are we + * to argue? Note that {whitespace_with_newline} should not have * after + * it, whereas {whitespace} should generally have a * after it... + */ + +special_whitespace ({space}+|{comment}{newline}) +horiz_whitespace ({horiz_space}|{comment}) +whitespace_with_newline ({horiz_whitespace}*{newline}{special_whitespace}*) + +/* + * To ensure that {quotecontinue} can be scanned without having to back up + * if the full pattern isn't matched, we include trailing whitespace in + * {quotestop}. This matches all cases where {quotecontinue} fails to match, + * except for {quote} followed by whitespace and just one "-" (not two, + * which would start a {comment}). To cover that we have {quotefail}. + * The actions for {quotestop} and {quotefail} must throw back characters + * beyond the quote proper. + */ +quote ' +quotestop {quote}{whitespace}* +quotecontinue {quote}{whitespace_with_newline}{quote} +quotefail {quote}{whitespace}*"-" + +/* Bit string + * It is tempting to scan the string for only those characters + * which are allowed. However, this leads to silently swallowed + * characters if illegal characters are included in the string. + * For example, if xbinside is [01] then B'ABCD' is interpreted + * as a zero-length string, and the ABCD' is lost! + * Better to pass the string forward and let the input routines + * validate the contents. + */ +xbstart [bB]{quote} +xbinside [^']* + +/* Hexadecimal number */ +xhstart [xX]{quote} +xhinside [^']* + +/* National character */ +xnstart [nN]{quote} + +/* Quoted string that allows backslash escapes */ +xestart [eE]{quote} +xeinside [^\\']+ +xeescape [\\][^0-7] +xeoctesc [\\][0-7]{1,3} +xehexesc [\\]x[0-9A-Fa-f]{1,2} + +/* Extended quote + * xqdouble implements embedded quote, '''' + */ +xqstart {quote} +xqdouble {quote}{quote} +xqinside [^']+ + +/* $foo$ style quotes ("dollar quoting") + * The quoted string starts with $foo$ where "foo" is an optional string + * in the form of an identifier, except that it may not contain "$", + * and extends to the first occurrence of an identical string. + * There is *no* processing of the quoted text. + * + * {dolqfailed} is an error rule to avoid scanner backup when {dolqdelim} + * fails to match its trailing "$". + */ +dolq_start [A-Za-z\200-\377_] +dolq_cont [A-Za-z\200-\377_0-9] +dolqdelim \$({dolq_start}{dolq_cont}*)?\$ +dolqfailed \${dolq_start}{dolq_cont}* +dolqinside [^$]+ + +/* Double quote + * Allows embedded spaces and other special characters into identifiers. + */ +dquote \" +xdstart {dquote} +xdstop {dquote} +xddouble {dquote}{dquote} +xdinside [^"]+ + +/* C-style comments + * + * The "extended comment" syntax closely resembles allowable operator syntax. + * The tricky part here is to get lex to recognize a string starting with + * slash-star as a comment, when interpreting it as an operator would produce + * a longer match --- remember lex will prefer a longer match! Also, if we + * have something like plus-slash-star, lex will think this is a 3-character + * operator whereas we want to see it as a + operator and a comment start. + * The solution is two-fold: + * 1. append {op_chars}* to xcstart so that it matches as much text as + * {operator} would. Then the tie-breaker (first matching rule of same + * length) ensures xcstart wins. We put back the extra stuff with yyless() + * in case it contains a star-slash that should terminate the comment. + * 2. In the operator rule, check for slash-star within the operator, and + * if found throw it back with yyless(). This handles the plus-slash-star + * problem. + * Dash-dash comments have similar interactions with the operator rule. + */ +xcstart \/\*{op_chars}* +xcstop \*+\/ +xcinside [^*/]+ + +digit [0-9] +ident_start [A-Za-z\200-\377_] +ident_cont [A-Za-z\200-\377_0-9\$] + +identifier {ident_start}{ident_cont}* + +typecast "::" + +/* + * "self" is the set of chars that should be returned as single-character + * tokens. "op_chars" is the set of chars that can make up "Op" tokens, + * which can be one or more characters long (but if a single-char token + * appears in the "self" set, it is not to be returned as an Op). Note + * that the sets overlap, but each has some chars that are not in the other. + * + * If you change either set, adjust the character lists appearing in the + * rule for "operator"! + */ +self [,()\[\].;\:\+\-\*\/\%\^\<\>\=] +op_chars [\~\!\@\#\^\&\|\`\?\+\-\*\/\%\<\>\=] +operator {op_chars}+ + +/* we no longer allow unary minus in numbers. + * instead we pass it separately to parser. there it gets + * coerced via doNegate() -- Leon aug 20 1999 + * + * {realfail1} and {realfail2} are added to prevent the need for scanner + * backup when the {real} rule fails to match completely. + */ + +integer {digit}+ +decimal (({digit}*\.{digit}+)|({digit}+\.{digit}*)) +real ({integer}|{decimal})[Ee][-+]?{digit}+ +realfail1 ({integer}|{decimal})[Ee] +realfail2 ({integer}|{decimal})[Ee][-+] + +param \${integer} + +other . + +/* + * Dollar quoted strings are totally opaque, and no escaping is done on them. + * Other quoted strings must allow some special characters such as single-quote + * and newline. + * Embedded single-quotes are implemented both in the SQL standard + * style of two adjacent single quotes "''" and in the Postgres/Java style + * of escaped-quote "\'". + * Other embedded escaped characters are matched explicitly and the leading + * backslash is dropped from the string. + * Note that xcstart must appear before operator, as explained above! + * Also whitespace (comment) must appear before operator. + */ + +%% + +{whitespace} { + SET_YYLLOC(); + yylval.val.str = yytext; + yylval.val.modificator = NULL; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_WHITESPACE; + } + +{comment} { + SET_YYLLOC(); + yylval.val.str = yytext; + yylval.val.modificator = "sc"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_COMMENT; + } + + +{xcstart} { + /* Set location in case of syntax error in comment */ + SET_YYLLOC(); + xcdepth = 0; + BEGIN(xc); + /* Put back any characters past slash-star; see above */ + startlit(); + addlitchar('/'); + addlitchar('*'); + + yyless(2); + } + +{xcstart} { + xcdepth++; + /* Put back any characters past slash-star; see above */ + addlitchar('/'); + addlitchar('*'); + + yyless(2); + } + +{xcstop} { + if (xcdepth <= 0) + { + BEGIN(INITIAL); + addlitchar('*'); + addlitchar('/'); + + yylval.val.str = litbufdup(); + yylval.val.modificator = "ec"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_COMMENT; + } + else + { + xcdepth--; + addlitchar('*'); + addlitchar('/'); + } + + } + +{xcinside} { + addlit(yytext, yyleng); + } + +{op_chars} { + addlit(yytext, yyleng); + } + +\*+ { + addlit(yytext, yyleng); + } + +<> { + yylval.val.str = litbufdup(); + yylval.val.modificator = "ecu"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_COMMENT; + + } + +{xbstart} { + /* Binary bit type. + * At some point we should simply pass the string + * forward to the parser and label it there. + * In the meantime, place a leading "b" on the string + * to mark it for the input routine as a binary string. + */ + SET_YYLLOC(); + BEGIN(xb); + startlit(); + addlitchar('b'); + } +{quotestop} | +{quotefail} { + yyless(1); + BEGIN(INITIAL); + yylval.val.str = litbufdup(); + yylval.val.modificator = "b"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_NCONST; + } +{xhinside} | +{xbinside} { + addlit(yytext, yyleng); + } +{quotecontinue} | +{quotecontinue} { + /* ignore */ + } +<> { + yylval.val.str = litbufdup(); + yylval.val.modificator = "bu"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_NCONST; + } + +{xhstart} { + /* Hexadecimal bit type. + * At some point we should simply pass the string + * forward to the parser and label it there. + * In the meantime, place a leading "x" on the string + * to mark it for the input routine as a hex string. + */ + SET_YYLLOC(); + BEGIN(xh); + startlit(); + addlitchar('x'); + } +{quotestop} | +{quotefail} { + yyless(1); + BEGIN(INITIAL); + yylval.val.str = litbufdup(); + yylval.val.modificator = "x"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_NCONST; + } +<> { + yylval.val.str = litbufdup(); + yylval.val.modificator = "xu"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_NCONST; + } + +{xnstart} { + /* National character. + * We will pass this along as a normal character string, + * but preceded with an internally-generated "NCHAR". + */ + const char *keyword; + int keycode; + + SET_YYLLOC(); + yyless(1); /* eat only 'n' this time */ + /* nchar had better be a keyword! */ + keyword = orafce_scan_keyword("nchar", &keycode); + Assert(keyword != NULL); + yylval.val.str = (char*) keyword; + yylval.val.keycode = keycode; + yylval.val.modificator = NULL; + yylval.val.sep = NULL; + return X_KEYWORD; + } + +{xqstart} { + SET_YYLLOC(); + BEGIN(xq); + extended_string = false; + startlit(); + } +{xestart} { + SET_YYLLOC(); + BEGIN(xe); + extended_string = true; + startlit(); + } +{quotestop} | +{quotefail} { + yyless(1); + BEGIN(INITIAL); + yylval.val.str = litbufdup(); + yylval.val.modificator = extended_string ? "es" : "qs"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_SCONST; + } +{xqdouble} { + addlitchar('\''); + } +{xqinside} { + addlit(yytext, yyleng); + } +{xeinside} { + addlit(yytext, yyleng); + } +{xeescape} { + addlitchar(unescape_single_char(yytext[1])); + } +{xeoctesc} { + unsigned char c = strtoul(yytext+1, NULL, 8); + + addlitchar(c); + } +{xehexesc} { + unsigned char c = strtoul(yytext+2, NULL, 16); + + addlitchar(c); + } +{quotecontinue} { + /* ignore */ + } +. { + /* This is only needed for \ just before EOF */ + addlitchar(yytext[0]); + } +<> { + yylval.val.str = litbufdup(); + yylval.val.modificator = extended_string ? "esu" : "qsu"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_SCONST; + } + +{dolqdelim} { + SET_YYLLOC(); + dolqstart = pstrdup(yytext); + BEGIN(xdolq); + startlit(); + } +{dolqfailed} { + /* throw back all but the initial "$" */ + yyless(1); + /* and treat it as {other} */ + yylval.val.str = yytext; + yylval.val.modificator = "dolqf"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_OTHERS; + } +{dolqdelim} { + if (strcmp(yytext, dolqstart) == 0) + { + yylval.val.sep = dolqstart; + yylval.val.modificator = "dolq"; + BEGIN(INITIAL); + yylval.val.str = litbufdup(); + yylval.val.keycode = -1; + return X_SCONST; + } + else + { + /* + * When we fail to match $...$ to dolqstart, transfer + * the $... part to the output, but put back the final + * $ for rescanning. Consider $delim$...$junk$delim$ + */ + addlit(yytext, yyleng-1); + yyless(yyleng-1); + } + } +{dolqinside} { + addlit(yytext, yyleng); + } +{dolqfailed} { + addlit(yytext, yyleng); + } +. { + /* This is only needed for inside the quoted text */ + addlitchar(yytext[0]); + } +<> { + yylval.val.sep = dolqstart; + yylval.val.modificator = "dolqu"; + yylval.val.str = litbufdup(); + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_SCONST; + } + +{xdstart} { + SET_YYLLOC(); + BEGIN(xd); + startlit(); + } +{xdstop} { + char *ident; + + BEGIN(INITIAL); + if (literallen == 0) + yyerror(NULL, "zero-length delimited identifier"); + ident = litbufdup(); + if (literallen >= NAMEDATALEN) + truncate_identifier(ident, literallen, true); + yylval.val.modificator = "dq"; + yylval.val.str = ident; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_IDENT; + } +{xddouble} { + addlitchar('"'); + } +{xdinside} { + addlit(yytext, yyleng); + } +<> { + yylval.val.modificator = "dqu"; + yylval.val.str = litbufdup(); + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_IDENT; + } +{typecast} { + SET_YYLLOC(); + yylval.val.modificator = "typecast"; + yylval.val.keycode = X_TYPECAST; + yylval.val.sep = NULL; + return X_OTHERS; + } + +{self} { + SET_YYLLOC(); + yylval.val.str = yytext; + yylval.val.modificator = "self"; + yylval.val.keycode = yytext[0]; + yylval.val.sep = NULL; + return X_OTHERS; + } + +{operator} { + /* + * Check for embedded slash-star or dash-dash; those + * are comment starts, so operator must stop there. + * Note that slash-star or dash-dash at the first + * character will match a prior rule, not this one. + */ + int nchars = yyleng; + char *slashstar = strstr(yytext, "/*"); + char *dashdash = strstr(yytext, "--"); + + if (slashstar && dashdash) + { + /* if both appear, take the first one */ + if (slashstar > dashdash) + slashstar = dashdash; + } + else if (!slashstar) + slashstar = dashdash; + if (slashstar) + nchars = slashstar - yytext; + + /* + * For SQL compatibility, '+' and '-' cannot be the + * last char of a multi-char operator unless the operator + * contains chars that are not in SQL operators. + * The idea is to lex '=-' as two operators, but not + * to forbid operator names like '?-' that could not be + * sequences of SQL operators. + */ + while (nchars > 1 && + (yytext[nchars-1] == '+' || + yytext[nchars-1] == '-')) + { + int ic; + + for (ic = nchars-2; ic >= 0; ic--) + { + if (strchr("~!@#^&|`?%", yytext[ic])) + break; + } + if (ic >= 0) + break; /* found a char that makes it OK */ + nchars--; /* else remove the +/-, and check again */ + } + + SET_YYLLOC(); + + if (nchars < yyleng) + { + /* Strip the unwanted chars from the token */ + yyless(nchars); + /* + * If what we have left is only one char, and it's + * one of the characters matching "self", then + * return it as a character token the same way + * that the "self" rule would have. + */ + if (nchars == 1 && + strchr(",()[].;:+-*/%^<>=", yytext[0])) + { + yylval.val.str = yytext; + yylval.val.modificator = NULL; + yylval.val.keycode = yytext[0]; + yylval.val.sep = NULL; + return X_OTHERS; + } + } + + /* + * Complain if operator is too long. Unlike the case + * for identifiers, we make this an error not a notice- + * and-truncate, because the odds are we are looking at + * a syntactic mistake anyway. + */ + if (nchars >= NAMEDATALEN) + yyerror(NULL, "operator too long"); + + /* Convert "!=" operator to "<>" for compatibility */ + yylval.val.modificator = NULL; + if (strcmp(yytext, "!=") == 0) + yylval.val.str = pstrdup("<>"); + else + yylval.val.str = pstrdup(yytext); + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_OP; + } + +{param} { + SET_YYLLOC(); + yylval.val.modificator = NULL; + yylval.val.str = yytext; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_PARAM; + } + +{integer} { + long val; + char* endptr; + + SET_YYLLOC(); + errno = 0; + val = strtol(yytext, &endptr, 10); + if (*endptr != '\0' || errno == ERANGE +#ifdef HAVE_LONG_INT_64 + /* if long > 32 bits, check for overflow of int4 */ + || val != (long) ((int32) val) +#endif + ) + { + /* integer too large, treat it as a float */ + yylval.val.str = pstrdup(yytext); + yylval.val.modificator = "f"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_NCONST; + } + yylval.val.str = yytext; + yylval.val.modificator = "i"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_NCONST; + } +{decimal} { + SET_YYLLOC(); + yylval.val.str = pstrdup(yytext); + yylval.val.modificator = "f"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_NCONST; + } +{real} { + SET_YYLLOC(); + yylval.val.str = pstrdup(yytext); + yylval.val.modificator = "f"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_NCONST; + } +{realfail1} { + /* + * throw back the [Ee], and treat as {decimal}. Note + * that it is possible the input is actually {integer}, + * but since this case will almost certainly lead to a + * syntax error anyway, we don't bother to distinguish. + */ + yyless(yyleng-1); + SET_YYLLOC(); + yylval.val.str = pstrdup(yytext); + yylval.val.modificator = "f"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_NCONST; + } +{realfail2} { + /* throw back the [Ee][+-], and proceed as above */ + yyless(yyleng-2); + SET_YYLLOC(); + yylval.val.str = pstrdup(yytext); + yylval.val.modificator = "f"; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_NCONST; + } + + +{identifier} { + char *ident; + const char *keyword; + int keycode; + + SET_YYLLOC(); + + /* nchar had better be a keyword! */ + keyword = orafce_scan_keyword("nchar", &keycode); + + /* Is it a keyword? */ + keyword = orafce_scan_keyword(yytext, &keycode); + if (keyword != NULL) + { + yylval.val.str = (char*) keyword; + yylval.val.keycode = keycode; + yylval.val.modificator = NULL; + yylval.val.sep = NULL; + return X_KEYWORD; + } + + /* + * No. Convert the identifier to lower case, and truncate + * if necessary. + */ + ident = downcase_truncate_identifier(yytext, yyleng, true); + yylval.val.str = ident; + yylval.val.modificator = NULL; + yylval.val.keycode = -1; + yylval.val.sep = NULL; + return X_IDENT; + } + +{other} { + SET_YYLLOC(); + yylval.val.str = yytext; + yylval.val.modificator = NULL; + yylval.val.keycode = yytext[0]; + yylval.val.sep = NULL; + return X_OTHERS; + } + +<> { + SET_YYLLOC(); + yyterminate(); + } + +%% + +/* + * lexer_errposition + * Report a lexical-analysis-time cursor position, if possible. + * + * This is expected to be used within an ereport() call. The return value + * is a dummy (always 0, in fact). + * + * Note that this can only be used for messages from the lexer itself, + * since it depends on scanbuf to still be valid. + */ +static int +lexer_errposition(void) +{ + int pos; + + /* Convert byte offset to character number */ + pos = _pg_mbstrlen_with_len(scanbuf, orafce_sql_yylval.val.lloc) + 1; + /* And pass it to the ereport mechanism */ + +#if PG_VERSION_NUM >= 130000 + + errposition(pos); + + return pos; + +#else + + return errposition(pos); + +#endif + +} + +/* + * yyerror + * Report a lexer or grammar error. + * + * The message's cursor position identifies the most recently lexed token. + * This is OK for syntax error messages from the Bison parser, because Bison + * parsers report error as soon as the first unparsable token is reached. + * Beware of using yyerror for other purposes, as the cursor position might + * be misleading! + */ +void +orafce_sql_yyerror(List **result, const char *message) +{ + const char *loc = scanbuf + orafce_sql_yylval.val.lloc; + + if (*loc == YY_END_OF_BUFFER_CHAR) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s at end of input", message), + lexer_errposition())); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s at or near \"%s\"", message, loc), + lexer_errposition())); + } +} + + +/* + * Called before any actual parsing is done + */ +void +orafce_sql_scanner_init(const char *str) +{ + Size slen = strlen(str); + + /* + * Might be left over after ereport() + */ + if (YY_CURRENT_BUFFER) + yy_delete_buffer(YY_CURRENT_BUFFER); + + /* + * Make a scan buffer with special termination needed by flex. + */ + scanbuflen = slen; + scanbuf = palloc(slen + 2); + memcpy(scanbuf, str, slen); + scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR; + scanbufhandle = yy_scan_buffer(scanbuf, slen + 2); + + /* initialize literal buffer to a reasonable but expansible size */ + literalalloc = 128; + literalbuf = (char *) palloc(literalalloc); + startlit(); + + BEGIN(INITIAL); +} + + +/* + * Called after parsing is done to clean up after fdate_scanner_init() + */ +void +orafce_sql_scanner_finish(void) +{ + yy_delete_buffer(scanbufhandle); + pfree(scanbuf); +} + +static void +addlit(char *ytext, int yleng) +{ + /* enlarge buffer if needed */ + if ((literallen+yleng) >= literalalloc) + { + do { + literalalloc *= 2; + } while ((literallen+yleng) >= literalalloc); + literalbuf = (char *) repalloc(literalbuf, literalalloc); + } + /* append new data, add trailing null */ + memcpy(literalbuf+literallen, ytext, yleng); + literallen += yleng; + literalbuf[literallen] = '\0'; +} + + +static void +addlitchar(unsigned char ychar) +{ + /* enlarge buffer if needed */ + if ((literallen+1) >= literalalloc) + { + literalalloc *= 2; + literalbuf = (char *) repalloc(literalbuf, literalalloc); + } + /* append new data, add trailing null */ + literalbuf[literallen] = ychar; + literallen += 1; + literalbuf[literallen] = '\0'; +} + + +/* + * One might be tempted to write pstrdup(literalbuf) instead of this, + * but for long literals this is much faster because the length is + * already known. + */ +static char * +litbufdup(void) +{ + char *new; + + new = palloc(literallen + 1); + memcpy(new, literalbuf, literallen+1); + return new; +} + + +static unsigned char +unescape_single_char(unsigned char c) +{ + switch (c) + { + case 'b': + return '\b'; + case 'f': + return '\f'; + case 'n': + return '\n'; + case 'r': + return '\r'; + case 't': + return '\t'; + default: + return c; + } +} + + diff --git a/src/postgres/third-party-extensions/orafce/utility.c b/src/postgres/third-party-extensions/orafce/utility.c new file mode 100644 index 000000000000..96594990d0ea --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/utility.c @@ -0,0 +1,210 @@ +/* + This code implements one part of functonality of + free available library PL/Vision. Please look www.quest.com + + Original author: Steven Feuerstein, 1996 - 2002 + PostgreSQL implementation author: Pavel Stehule, 2006-2018 + + This module is under BSD Licence + + History: + 1.0. first public version 22. September 2006 + +*/ + +#include "postgres.h" +#include "utils/builtins.h" +#include "utils/numeric.h" +#include "string.h" +#include "stdlib.h" +#include "utils/pg_locale.h" +#include "mb/pg_wchar.h" +#include "lib/stringinfo.h" + +#include "catalog/pg_type.h" +#include "libpq/pqformat.h" +#include "utils/array.h" +#include "utils/memutils.h" +#include "utils/lsyscache.h" +#include "access/tupmacs.h" +#include "orafce.h" +#include "builtins.h" + +#include "utils/elog.h" + +PG_FUNCTION_INFO_V1(dbms_utility_format_call_stack0); +PG_FUNCTION_INFO_V1(dbms_utility_format_call_stack1); + +static char* +dbms_utility_format_call_stack(char mode) +{ + MemoryContext oldcontext = CurrentMemoryContext; + ErrorData *edata; + ErrorContextCallback *econtext; + StringInfo sinfo; + + +#if PG_VERSION_NUM >= 130000 + + errstart(ERROR, TEXTDOMAIN); + +#else + + errstart(ERROR, __FILE__, __LINE__, PG_FUNCNAME_MACRO, TEXTDOMAIN); + +#endif + + MemoryContextSwitchTo(oldcontext); + + for (econtext = error_context_stack; + econtext != NULL; + econtext = econtext->previous) + (*econtext->callback) (econtext->arg); + + edata = CopyErrorData(); + + FlushErrorState(); + + /* Now I wont to parse edata->context to more traditional format */ + /* I am not sure about order */ + + sinfo = makeStringInfo(); + + switch (mode) + { + case 'o': + appendStringInfoString(sinfo, "----- PL/pgSQL Call Stack -----\n"); + appendStringInfoString(sinfo, " object line object\n"); + appendStringInfoString(sinfo, " handle number name\n"); + break; + } + + if (edata->context) + { + char *start = edata->context; + while (*start) + { + char *oname = "anonymous object"; + char *line = ""; + char *eol = strchr(start, '\n'); + Oid fnoid = InvalidOid; + + /* first, solve multilines */ + if (eol) + *eol = '\0'; + + /* first know format */ + if (strncmp(start, "PL/pgSQL function ",18) == 0) + { + char *p1, *p2; + + if ((p1 = strstr(start, "function \""))) + { + p1 += strlen("function \""); + + if ((p2 = strchr(p1, '"'))) + { + *p2++ = '\0'; + oname = p1; + start = p2; + } + } + else if ((p1 = strstr(start, "function "))) + { + p1 += strlen("function "); + + if ((p2 = strchr(p1, ')'))) + { + char c = *++p2; + *p2 = '\0'; + + oname = pstrdup(p1); + fnoid = DatumGetObjectId(DirectFunctionCall1(regprocedurein, + CStringGetDatum(oname))); + *p2 = c; + start = p2; + } + } + + + if ((p1 = strstr(start, "line "))) + { + size_t p2i; + char c; + + p1 += strlen("line "); + p2i = strspn(p1, "0123456789"); + + /* safe separator */ + c = p1[p2i]; + + p1[p2i] = '\0'; + line = pstrdup(p1); + p1[p2i] = c; + } + } + + switch (mode) + { + case 'o': + appendStringInfo(sinfo, "%8x %5s function %s", (int)fnoid, line, oname); + break; + + case 'p': + appendStringInfo(sinfo, "%8d %5s function %s", (int)fnoid, line, oname); + break; + + case 's': + appendStringInfo(sinfo, "%d,%s,%s", (int)fnoid, line, oname); + break; + } + + if (eol) + { + start = eol + 1; + appendStringInfoChar(sinfo, '\n'); + } + else + break; + } + + } + + return sinfo->data; +} + + +Datum +dbms_utility_format_call_stack0(PG_FUNCTION_ARGS) +{ + PG_RETURN_TEXT_P(cstring_to_text(dbms_utility_format_call_stack('o'))); +} + +Datum +dbms_utility_format_call_stack1(PG_FUNCTION_ARGS) +{ + text *arg = PG_GETARG_TEXT_P(0); + char mode; + + if ((1 != VARSIZE(arg) - VARHDRSZ)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid parameter"), + errdetail("Allowed only chars [ops]."))); + + mode = *VARDATA(arg); + switch (mode) + { + case 'o': + case 'p': + case 's': + break; + default: + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid parameter"), + errdetail("Allowed only chars [ops]."))); + } + + PG_RETURN_TEXT_P(cstring_to_text(dbms_utility_format_call_stack(mode))); +} diff --git a/src/postgres/third-party-extensions/orafce/varchar2.c b/src/postgres/third-party-extensions/orafce/varchar2.c new file mode 100644 index 000000000000..cd155d315871 --- /dev/null +++ b/src/postgres/third-party-extensions/orafce/varchar2.c @@ -0,0 +1,255 @@ +/*---------------------------------------------------------------------------- + * + * varchar2.c + * VARCHAR2 type for PostgreSQL. + * + *---------------------------------------------------------------------------- + */ + +#include "postgres.h" +#include "access/hash.h" +#include "libpq/pqformat.h" +#include "nodes/nodeFuncs.h" +#include "utils/array.h" +#include "utils/builtins.h" +#include "mb/pg_wchar.h" +#include "fmgr.h" + +#include "orafce.h" +#include "builtins.h" + +PG_FUNCTION_INFO_V1(varchar2in); +PG_FUNCTION_INFO_V1(varchar2out); +PG_FUNCTION_INFO_V1(varchar2); +PG_FUNCTION_INFO_V1(varchar2recv); +PG_FUNCTION_INFO_V1(orafce_concat2); +PG_FUNCTION_INFO_V1(orafce_varchar_transform); + +bool orafce_varchar2_null_safe_concat = false; + + +/* + * varchar2_input -- common guts of varchar2in and varchar2recv + * + * s is the input text of length len (may not be null-terminated) + * atttypmod is the typmod value to apply + * + * If the input string is too long, raise an error + * + * Uses the C string to text conversion function, which is only appropriate + * if VarChar and text are equivalent types. + */ + +static VarChar * +varchar2_input(const char *s, size_t len, int32 atttypmod) +{ + VarChar *result; /* input data */ + size_t maxlen; + + maxlen = atttypmod - VARHDRSZ; + + /* + * Perform the typmod check; error out if value too long for VARCHAR2 + */ + if (atttypmod >= (int32) VARHDRSZ && len > maxlen) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("input value length is %zd; too long for type varchar2(%zd)", len , maxlen))); + + result = (VarChar *) cstring_to_text_with_len(s, size2int(len)); + return result; +} + +/* + * Converts a C string to VARCHAR2 internal representation. atttypmod + * is the declared length of the type plus VARHDRSZ. + */ +Datum +varchar2in(PG_FUNCTION_ARGS) +{ + char *s = PG_GETARG_CSTRING(0); +#ifdef NOT_USED + Oid typelem = PG_GETARG_OID(1); +#endif + int32 atttypmod = PG_GETARG_INT32(2); + VarChar *result; + + result = varchar2_input(s, strlen(s), atttypmod); + PG_RETURN_VARCHAR_P(result); +} + + +/* + * converts a VARCHAR2 value to a C string. + * + * Uses the text to C string conversion function, which is only appropriate + * if VarChar and text are equivalent types. + */ +Datum +varchar2out(PG_FUNCTION_ARGS) +{ + Datum txt = PG_GETARG_DATUM(0); + + PG_RETURN_CSTRING(TextDatumGetCString(txt)); +} + +/* + * converts external binary format to varchar + */ +Datum +varchar2recv(PG_FUNCTION_ARGS) +{ + StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); + +#ifdef NOT_USED + Oid typelem = PG_GETARG_OID(1); +#endif + int32 atttypmod = PG_GETARG_INT32(2); /* typmod of the receiving column */ + VarChar *result; + char *str; /* received data */ + int nbytes; /* length in bytes of recived data */ + + str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes); + result = varchar2_input(str, nbytes, atttypmod); + pfree(str); + PG_RETURN_VARCHAR_P(result); +} + + +/* + * varchar2send -- convert varchar2 to binary value + * + * just use varcharsend() + */ + +/* + * varchar2_transform() + * Flatten calls to varchar's length coercion function that set the new maximum + * length >= the previous maximum length. We can ignore the isExplicit + * argument, since that only affects truncation cases. + * + * just use varchar_transform() + */ + +Datum +orafce_varchar_transform(PG_FUNCTION_ARGS) +{ +#if PG_VERSION_NUM < 120000 + + return varchar_transform(fcinfo); + +#else + + return varchar_support(fcinfo); + +#endif +} + + +/* + * Converts a VARCHAR2 type to the specified size. + * + * maxlen is the typmod, ie, declared length plus VARHDRSZ bytes. + * isExplicit is true if this is for an explicit cast to varchar2(N). + * + * Truncation rules: for an explicit cast, silently truncate to the given + * length; for an implicit cast, raise error if length limit is exceeded + */ +Datum +varchar2(PG_FUNCTION_ARGS) +{ + VarChar *source = PG_GETARG_VARCHAR_PP(0); + int32 typmod = PG_GETARG_INT32(1); + bool isExplicit = PG_GETARG_BOOL(2); + int32 len, + maxlen; + char *s_data; + + len = VARSIZE_ANY_EXHDR(source); + s_data = VARDATA_ANY(source); + maxlen = typmod - VARHDRSZ; + + /* No work if typmod is invalid or supplied data fits it already */ + if (maxlen < 0 || len <= maxlen) + PG_RETURN_VARCHAR_P(source); + + /* error out if value too long unless it's an explicit cast */ + if (!isExplicit) + { + if (len > maxlen) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("input value length is %d; too long for type varchar2(%d)",len ,maxlen))); + } + + PG_RETURN_VARCHAR_P((VarChar *) cstring_to_text_with_len(s_data,maxlen)); +} + + +/* + * varchar2typmodin -- type modifier input function + * + * just use varchartypmodin() + */ + +/* + * varchar2typmodout -- type modifier output function + * + * just use varchartypmodout() + */ + + +/* + * orafce_concat2 - null safe concat + * + * returns NULL instead empty string + */ +Datum +orafce_concat2(PG_FUNCTION_ARGS) +{ + text *arg1 = NULL, + *arg2 = NULL, + *result; + int32 len1 = 0, + len2 = 0, + len; + char *ptr; + + if (!PG_ARGISNULL(0)) + { + arg1 = PG_GETARG_TEXT_PP(0); + len1 = VARSIZE_ANY_EXHDR(arg1); + } + if (!PG_ARGISNULL(1)) + { + arg2 = PG_GETARG_TEXT_PP(1); + len2 = VARSIZE_ANY_EXHDR(arg2); + } + + /* default behave should be compatible with Postgres */ + if (!orafce_varchar2_null_safe_concat) + { + if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) + PG_RETURN_NULL(); + } + else + { + if (len1 == 0 && len2 == 0) + PG_RETURN_NULL(); + } + + /* hard work, we should to concat strings */ + len = len1 + len2 + VARHDRSZ; + + result = (text *) palloc(len); + SET_VARSIZE(result, len); + + ptr = VARDATA(result); + + if (len1 > 0) + memcpy(ptr, VARDATA_ANY(arg1), len1); + if (len2 > 0) + memcpy(ptr + len1, VARDATA_ANY(arg2), len2); + + PG_RETURN_TEXT_P(result); +}