From dfa1e20727051af2e4907bc15bab08ae6cd4e5fc Mon Sep 17 00:00:00 2001 From: lvhui Date: Sat, 25 Dec 2021 09:53:26 +0800 Subject: [PATCH] The official version of orafce-3.17 --- contrib/orafce/COPYRIGHT.orafce | 14 + contrib/orafce/INSTALL.orafce | 27 + contrib/orafce/META.json | 56 + contrib/orafce/Makefile | 69 + contrib/orafce/NEWS | 72 + contrib/orafce/README.asciidoc | 812 +++ contrib/orafce/README.msvc | 51 + contrib/orafce/aggregate.c | 374 ++ contrib/orafce/alert.c | 1081 ++++ contrib/orafce/assert.c | 404 ++ contrib/orafce/assert.h | 9 + contrib/orafce/builtins.h | 295 ++ contrib/orafce/charlen.c | 39 + contrib/orafce/charpad.c | 486 ++ contrib/orafce/convert.c | 933 ++++ contrib/orafce/datefce.c | 1060 ++++ .../Orafce_Documentation_01.md | 142 + .../Orafce_Documentation_02.md | 98 + .../Orafce_Documentation_03.md | 118 + .../Orafce_Documentation_04.md | 27 + .../Orafce_Documentation_05.md | 2258 +++++++++ .../Orafce_Documentation_06.md | 2000 ++++++++ .../Orafce_Documentation_07.md | 66 + .../orafce_documentation/gif/ADD_MONTHS.gif | Bin 0 -> 2359 bytes .../doc/orafce_documentation/gif/BITAND.gif | Bin 0 -> 1940 bytes .../doc/orafce_documentation/gif/BTRIM.gif | Bin 0 -> 2448 bytes .../doc/orafce_documentation/gif/COSH.gif | Bin 0 -> 1855 bytes .../doc/orafce_documentation/gif/DATE.gif | Bin 0 -> 1214 bytes .../orafce_documentation/gif/DBMS_ALERT.gif | Bin 0 -> 7138 bytes .../gif/DBMS_ALERT_flow.gif | Bin 0 -> 4959 bytes .../orafce_documentation/gif/DBMS_ASSERT.gif | Bin 0 -> 8473 bytes .../orafce_documentation/gif/DBMS_OUTPUT.gif | Bin 0 -> 7495 bytes .../orafce_documentation/gif/DBMS_PIPE.gif | Bin 0 -> 18758 bytes .../gif/DBMS_PIPE_flow.gif | Bin 0 -> 12527 bytes .../orafce_documentation/gif/DBMS_RANDOM.gif | Bin 0 -> 7986 bytes .../orafce_documentation/gif/DBMS_UTILITY.gif | Bin 0 -> 2178 bytes .../orafce_documentation/gif/DBTIMEZONE.gif | Bin 0 -> 1684 bytes .../doc/orafce_documentation/gif/DECODE.gif | Bin 0 -> 3389 bytes .../doc/orafce_documentation/gif/DUMP.gif | Bin 0 -> 2231 bytes .../doc/orafce_documentation/gif/INSTR.gif | Bin 0 -> 3448 bytes .../doc/orafce_documentation/gif/LAST_DAY.gif | Bin 0 -> 1724 bytes .../doc/orafce_documentation/gif/LENGTH.gif | Bin 0 -> 1812 bytes .../doc/orafce_documentation/gif/LENGTHB.gif | Bin 0 -> 1811 bytes .../doc/orafce_documentation/gif/LISTAGG.gif | Bin 0 -> 2607 bytes .../doc/orafce_documentation/gif/LNNVL.gif | Bin 0 -> 1745 bytes .../doc/orafce_documentation/gif/LPAD.gif | Bin 0 -> 2835 bytes .../doc/orafce_documentation/gif/LTRIM.gif | Bin 0 -> 2438 bytes .../doc/orafce_documentation/gif/MEDIAN.gif | Bin 0 -> 1912 bytes .../gif/MONTHS_BETWEEN.gif | Bin 0 -> 2295 bytes .../doc/orafce_documentation/gif/NANVL.gif | Bin 0 -> 2999 bytes .../doc/orafce_documentation/gif/NEXT_DAY.gif | Bin 0 -> 2310 bytes .../doc/orafce_documentation/gif/NLSSORT.gif | Bin 0 -> 2366 bytes .../orafce_documentation/gif/NVARCHAR2.gif | Bin 0 -> 1947 bytes .../doc/orafce_documentation/gif/NVL.gif | Bin 0 -> 2095 bytes .../doc/orafce_documentation/gif/NVL2.gif | Bin 0 -> 2651 bytes .../doc/orafce_documentation/gif/ROUND.gif | Bin 0 -> 2227 bytes .../doc/orafce_documentation/gif/RPAD.gif | Bin 0 -> 2669 bytes .../doc/orafce_documentation/gif/RTRIM.gif | Bin 0 -> 2439 bytes .../gif/SESSIONTIMEZONE.gif | Bin 0 -> 1766 bytes .../doc/orafce_documentation/gif/SINH.gif | Bin 0 -> 1838 bytes .../doc/orafce_documentation/gif/STRPOSB.gif | Bin 0 -> 2017 bytes .../doc/orafce_documentation/gif/SUBSTR.gif | Bin 0 -> 1981 bytes .../doc/orafce_documentation/gif/SUBSTRB.gif | Bin 0 -> 2862 bytes .../doc/orafce_documentation/gif/SYSDATE.gif | Bin 0 -> 1561 bytes .../doc/orafce_documentation/gif/TANH.gif | Bin 0 -> 1906 bytes .../doc/orafce_documentation/gif/TO_CHAR.gif | Bin 0 -> 2584 bytes .../doc/orafce_documentation/gif/TO_DATE.gif | Bin 0 -> 2400 bytes .../gif/TO_MULTI_BYTE.gif | Bin 0 -> 1848 bytes .../orafce_documentation/gif/TO_NUMBER.gif | Bin 0 -> 3122 bytes .../gif/TO_SINGLE_BYTE.gif | Bin 0 -> 2033 bytes .../doc/orafce_documentation/gif/TRUNC.gif | Bin 0 -> 2211 bytes .../doc/orafce_documentation/gif/Thumbs.db | Bin 0 -> 248832 bytes .../doc/orafce_documentation/gif/UTL_FILE.gif | Bin 0 -> 23701 bytes .../doc/orafce_documentation/gif/VARCHAR2.gif | Bin 0 -> 1986 bytes .../gif/Inventory_Management_Database.gif | Bin 0 -> 10307 bytes .../orafce/doc/sql_migration/gif/STRPOSB.gif | Bin 0 -> 2205 bytes .../gif/Staff_Management_Database.gif | Bin 0 -> 8645 bytes .../orafce/doc/sql_migration/gif/Thumbs.db | Bin 0 -> 27136 bytes .../doc/sql_migration/sql_migration00.md | 65 + .../doc/sql_migration/sql_migration01.md | 420 ++ .../doc/sql_migration/sql_migration02.md | 759 +++ .../doc/sql_migration/sql_migration03.md | 493 ++ .../doc/sql_migration/sql_migration04.md | 1650 ++++++ .../doc/sql_migration/sql_migration05.md | 1872 +++++++ .../doc/sql_migration/sql_migration06.md | 2605 ++++++++++ .../doc/sql_migration/sql_migration07.md | 265 + contrib/orafce/expected/aggregates.out | 126 + .../orafce/expected/dbms_alert_session_A.out | 165 + .../orafce/expected/dbms_alert_session_B.out | 151 + .../orafce/expected/dbms_alert_session_C.out | 35 + contrib/orafce/expected/dbms_output.out | 1043 ++++ .../orafce/expected/dbms_pipe_session_A.out | 113 + .../orafce/expected/dbms_pipe_session_B.out | 248 + contrib/orafce/expected/dbms_random.out | 91 + contrib/orafce/expected/dbms_random_1.out | 91 + contrib/orafce/expected/dbms_utility.out | 17 + contrib/orafce/expected/files.out | 256 + contrib/orafce/expected/files_1.out | 256 + contrib/orafce/expected/init.out | 1 + contrib/orafce/expected/nlssort.out | 60 + contrib/orafce/expected/nvarchar2.out | 67 + contrib/orafce/expected/orafce.out | 4402 +++++++++++++++++ contrib/orafce/expected/orafce2.out | 4 + contrib/orafce/expected/orafce2_1.out | 4 + contrib/orafce/expected/regexp_func.out | 930 ++++ contrib/orafce/expected/varchar2.out | 149 + contrib/orafce/file.c | 1190 +++++ contrib/orafce/magic.c | 6 + contrib/orafce/msvc/orafce.2010.sln | 44 + contrib/orafce/msvc/orafce.2010.vcxproj | 523 ++ .../orafce/msvc/orafce.2010.vcxproj.filters | 146 + contrib/orafce/nvarchar2.c | 199 + contrib/orafce/orafce--3.10--3.11.sql | 3 + contrib/orafce/orafce--3.11--3.12.sql | 9 + contrib/orafce/orafce--3.12--3.13.sql | 0 contrib/orafce/orafce--3.13--3.14.sql | 34 + contrib/orafce/orafce--3.14--3.15.sql | 632 +++ contrib/orafce/orafce--3.15--3.16.sql | 5 + contrib/orafce/orafce--3.16--3.17.sql | 685 +++ contrib/orafce/orafce--3.17.sql | 4244 ++++++++++++++++ contrib/orafce/orafce--3.2--3.3.sql | 71 + contrib/orafce/orafce--3.3--3.4.sql | 14 + contrib/orafce/orafce--3.4--3.5.sql | 7 + contrib/orafce/orafce--3.5--3.6.sql | 188 + contrib/orafce/orafce--3.6--3.7.sql | 57 + contrib/orafce/orafce--3.7--3.8.sql | 21 + contrib/orafce/orafce--3.8--3.9.sql | 13 + contrib/orafce/orafce--3.9--3.10.sql | 1 + contrib/orafce/orafce.c | 57 + contrib/orafce/orafce.control | 5 + contrib/orafce/orafce.h | 65 + contrib/orafce/others.c | 559 +++ contrib/orafce/parallel_schedule | 3 + contrib/orafce/parse_keyword.c | 51 + contrib/orafce/parse_keyword.h | 2 + contrib/orafce/pipe.c | 1365 +++++ contrib/orafce/pipe.h | 50 + contrib/orafce/plunit.c | 435 ++ contrib/orafce/plvdate.c | 921 ++++ contrib/orafce/plvlex.c | 292 ++ contrib/orafce/plvlex.h | 10 + contrib/orafce/plvstr.c | 1344 +++++ contrib/orafce/plvsubst.c | 277 ++ contrib/orafce/putline.c | 366 ++ contrib/orafce/random.c | 363 ++ contrib/orafce/replace_empty_string.c | 339 ++ contrib/orafce/shmmc.c | 347 ++ contrib/orafce/shmmc.h | 12 + contrib/orafce/sql/aggregates.sql | 89 + contrib/orafce/sql/dbms_alert_session_A.sql | 66 + contrib/orafce/sql/dbms_alert_session_B.sql | 49 + contrib/orafce/sql/dbms_alert_session_C.sql | 15 + contrib/orafce/sql/dbms_output.sql | 764 +++ contrib/orafce/sql/dbms_pipe.sql | 58 + contrib/orafce/sql/dbms_pipe_session_A.sql | 206 + contrib/orafce/sql/dbms_pipe_session_B.sql | 165 + contrib/orafce/sql/dbms_random.sql | 16 + contrib/orafce/sql/dbms_utility.sql | 82 + contrib/orafce/sql/files.sql | 138 + contrib/orafce/sql/init.sql | 5 + contrib/orafce/sql/nlssort.sql | 21 + contrib/orafce/sql/nvarchar2.sql | 62 + contrib/orafce/sql/orafce.sql | 1033 ++++ contrib/orafce/sql/orafce2.sql | 3 + contrib/orafce/sql/regexp_func.sql | 297 ++ contrib/orafce/sql/varchar2.sql | 101 + contrib/orafce/sqlparse.c | 1498 ++++++ contrib/orafce/sqlparse.h | 116 + contrib/orafce/sqlparse.y | 114 + contrib/orafce/sqlscan.c | 3327 +++++++++++++ contrib/orafce/sqlscan.l | 1041 ++++ contrib/orafce/utility.c | 227 + contrib/orafce/varchar2.c | 255 + 173 files changed, 51532 insertions(+) create mode 100644 contrib/orafce/COPYRIGHT.orafce create mode 100644 contrib/orafce/INSTALL.orafce create mode 100644 contrib/orafce/META.json create mode 100644 contrib/orafce/Makefile create mode 100644 contrib/orafce/NEWS create mode 100644 contrib/orafce/README.asciidoc create mode 100644 contrib/orafce/README.msvc create mode 100644 contrib/orafce/aggregate.c create mode 100644 contrib/orafce/alert.c create mode 100644 contrib/orafce/assert.c create mode 100644 contrib/orafce/assert.h create mode 100644 contrib/orafce/builtins.h create mode 100644 contrib/orafce/charlen.c create mode 100644 contrib/orafce/charpad.c create mode 100644 contrib/orafce/convert.c create mode 100644 contrib/orafce/datefce.c create mode 100644 contrib/orafce/doc/orafce_documentation/Orafce_Documentation_01.md create mode 100644 contrib/orafce/doc/orafce_documentation/Orafce_Documentation_02.md create mode 100644 contrib/orafce/doc/orafce_documentation/Orafce_Documentation_03.md create mode 100644 contrib/orafce/doc/orafce_documentation/Orafce_Documentation_04.md create mode 100644 contrib/orafce/doc/orafce_documentation/Orafce_Documentation_05.md create mode 100644 contrib/orafce/doc/orafce_documentation/Orafce_Documentation_06.md create mode 100644 contrib/orafce/doc/orafce_documentation/Orafce_Documentation_07.md create mode 100644 contrib/orafce/doc/orafce_documentation/gif/ADD_MONTHS.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/BITAND.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/BTRIM.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/COSH.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/DATE.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/DBMS_ALERT.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/DBMS_ALERT_flow.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/DBMS_ASSERT.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/DBMS_OUTPUT.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/DBMS_PIPE.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/DBMS_PIPE_flow.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/DBMS_RANDOM.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/DBMS_UTILITY.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/DBTIMEZONE.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/DECODE.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/DUMP.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/INSTR.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/LAST_DAY.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/LENGTH.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/LENGTHB.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/LISTAGG.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/LNNVL.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/LPAD.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/LTRIM.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/MEDIAN.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/MONTHS_BETWEEN.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/NANVL.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/NEXT_DAY.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/NLSSORT.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/NVARCHAR2.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/NVL.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/NVL2.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/ROUND.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/RPAD.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/RTRIM.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/SESSIONTIMEZONE.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/SINH.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/STRPOSB.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/SUBSTR.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/SUBSTRB.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/SYSDATE.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/TANH.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/TO_CHAR.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/TO_DATE.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/TO_MULTI_BYTE.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/TO_NUMBER.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/TO_SINGLE_BYTE.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/TRUNC.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/Thumbs.db create mode 100644 contrib/orafce/doc/orafce_documentation/gif/UTL_FILE.gif create mode 100644 contrib/orafce/doc/orafce_documentation/gif/VARCHAR2.gif create mode 100644 contrib/orafce/doc/sql_migration/gif/Inventory_Management_Database.gif create mode 100644 contrib/orafce/doc/sql_migration/gif/STRPOSB.gif create mode 100644 contrib/orafce/doc/sql_migration/gif/Staff_Management_Database.gif create mode 100644 contrib/orafce/doc/sql_migration/gif/Thumbs.db create mode 100644 contrib/orafce/doc/sql_migration/sql_migration00.md create mode 100644 contrib/orafce/doc/sql_migration/sql_migration01.md create mode 100644 contrib/orafce/doc/sql_migration/sql_migration02.md create mode 100644 contrib/orafce/doc/sql_migration/sql_migration03.md create mode 100644 contrib/orafce/doc/sql_migration/sql_migration04.md create mode 100644 contrib/orafce/doc/sql_migration/sql_migration05.md create mode 100644 contrib/orafce/doc/sql_migration/sql_migration06.md create mode 100644 contrib/orafce/doc/sql_migration/sql_migration07.md create mode 100644 contrib/orafce/expected/aggregates.out create mode 100644 contrib/orafce/expected/dbms_alert_session_A.out create mode 100644 contrib/orafce/expected/dbms_alert_session_B.out create mode 100644 contrib/orafce/expected/dbms_alert_session_C.out create mode 100644 contrib/orafce/expected/dbms_output.out create mode 100644 contrib/orafce/expected/dbms_pipe_session_A.out create mode 100644 contrib/orafce/expected/dbms_pipe_session_B.out create mode 100644 contrib/orafce/expected/dbms_random.out create mode 100644 contrib/orafce/expected/dbms_random_1.out create mode 100644 contrib/orafce/expected/dbms_utility.out create mode 100644 contrib/orafce/expected/files.out create mode 100644 contrib/orafce/expected/files_1.out create mode 100644 contrib/orafce/expected/init.out create mode 100644 contrib/orafce/expected/nlssort.out create mode 100644 contrib/orafce/expected/nvarchar2.out create mode 100644 contrib/orafce/expected/orafce.out create mode 100644 contrib/orafce/expected/orafce2.out create mode 100644 contrib/orafce/expected/orafce2_1.out create mode 100644 contrib/orafce/expected/regexp_func.out create mode 100644 contrib/orafce/expected/varchar2.out create mode 100644 contrib/orafce/file.c create mode 100644 contrib/orafce/magic.c create mode 100644 contrib/orafce/msvc/orafce.2010.sln create mode 100644 contrib/orafce/msvc/orafce.2010.vcxproj create mode 100644 contrib/orafce/msvc/orafce.2010.vcxproj.filters create mode 100644 contrib/orafce/nvarchar2.c create mode 100644 contrib/orafce/orafce--3.10--3.11.sql create mode 100644 contrib/orafce/orafce--3.11--3.12.sql create mode 100644 contrib/orafce/orafce--3.12--3.13.sql create mode 100644 contrib/orafce/orafce--3.13--3.14.sql create mode 100644 contrib/orafce/orafce--3.14--3.15.sql create mode 100644 contrib/orafce/orafce--3.15--3.16.sql create mode 100644 contrib/orafce/orafce--3.16--3.17.sql create mode 100644 contrib/orafce/orafce--3.17.sql create mode 100644 contrib/orafce/orafce--3.2--3.3.sql create mode 100644 contrib/orafce/orafce--3.3--3.4.sql create mode 100644 contrib/orafce/orafce--3.4--3.5.sql create mode 100644 contrib/orafce/orafce--3.5--3.6.sql create mode 100644 contrib/orafce/orafce--3.6--3.7.sql create mode 100644 contrib/orafce/orafce--3.7--3.8.sql create mode 100644 contrib/orafce/orafce--3.8--3.9.sql create mode 100644 contrib/orafce/orafce--3.9--3.10.sql create mode 100644 contrib/orafce/orafce.c create mode 100644 contrib/orafce/orafce.control create mode 100644 contrib/orafce/orafce.h create mode 100644 contrib/orafce/others.c create mode 100644 contrib/orafce/parallel_schedule create mode 100644 contrib/orafce/parse_keyword.c create mode 100644 contrib/orafce/parse_keyword.h create mode 100644 contrib/orafce/pipe.c create mode 100644 contrib/orafce/pipe.h create mode 100644 contrib/orafce/plunit.c create mode 100644 contrib/orafce/plvdate.c create mode 100644 contrib/orafce/plvlex.c create mode 100644 contrib/orafce/plvlex.h create mode 100644 contrib/orafce/plvstr.c create mode 100644 contrib/orafce/plvsubst.c create mode 100644 contrib/orafce/putline.c create mode 100644 contrib/orafce/random.c create mode 100644 contrib/orafce/replace_empty_string.c create mode 100644 contrib/orafce/shmmc.c create mode 100644 contrib/orafce/shmmc.h create mode 100644 contrib/orafce/sql/aggregates.sql create mode 100644 contrib/orafce/sql/dbms_alert_session_A.sql create mode 100644 contrib/orafce/sql/dbms_alert_session_B.sql create mode 100644 contrib/orafce/sql/dbms_alert_session_C.sql create mode 100644 contrib/orafce/sql/dbms_output.sql create mode 100644 contrib/orafce/sql/dbms_pipe.sql create mode 100644 contrib/orafce/sql/dbms_pipe_session_A.sql create mode 100644 contrib/orafce/sql/dbms_pipe_session_B.sql create mode 100644 contrib/orafce/sql/dbms_random.sql create mode 100644 contrib/orafce/sql/dbms_utility.sql create mode 100644 contrib/orafce/sql/files.sql create mode 100644 contrib/orafce/sql/init.sql create mode 100644 contrib/orafce/sql/nlssort.sql create mode 100644 contrib/orafce/sql/nvarchar2.sql create mode 100644 contrib/orafce/sql/orafce.sql create mode 100644 contrib/orafce/sql/orafce2.sql create mode 100644 contrib/orafce/sql/regexp_func.sql create mode 100644 contrib/orafce/sql/varchar2.sql create mode 100644 contrib/orafce/sqlparse.c create mode 100644 contrib/orafce/sqlparse.h create mode 100644 contrib/orafce/sqlparse.y create mode 100644 contrib/orafce/sqlscan.c create mode 100644 contrib/orafce/sqlscan.l create mode 100644 contrib/orafce/utility.c create mode 100644 contrib/orafce/varchar2.c diff --git a/contrib/orafce/COPYRIGHT.orafce b/contrib/orafce/COPYRIGHT.orafce new file mode 100644 index 000000000..6cae1c8b7 --- /dev/null +++ b/contrib/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/contrib/orafce/INSTALL.orafce b/contrib/orafce/INSTALL.orafce new file mode 100644 index 000000000..88dfa8244 --- /dev/null +++ b/contrib/orafce/INSTALL.orafce @@ -0,0 +1,27 @@ +Installation +============ + +This module is normally distributed as a PostgreSQL 'contrib' module. To +install it from a pre-configured source tree run the following commands +as a user with appropriate privileges from the orafce source directory: + +export NO_PGXS=1 +make +make install + +Alternatively, if you have no source tree you can install using PGXS. Simply +run the following commands the adminpack source directory: + +make +make install + +To install Orafce functions in the database, either run the orafce.sql script +using the pgAdmin SQL tool (and then close and reopen the connection to the +freshly instrumented server), or run the script using psql, eg: + +CREATE EXTENSION orafce; + +Other administration tools that use this module may have different requirements, +please consult the tool's documentation for further details. + +This package requires PostgreSQL 9.5 or later. diff --git a/contrib/orafce/META.json b/contrib/orafce/META.json new file mode 100644 index 000000000..a827f1935 --- /dev/null +++ b/contrib/orafce/META.json @@ -0,0 +1,56 @@ +{ + "name": "orafce", + "abstract": "Oracle's compatibility functions and packages", + "description": "This module allows use a well known Oracle's functions and packages inside PostgreSQL", + "version": "3.17.0", + "maintainer": [ + "Pavel Stehule ", + "Takahiro Itagaki " + ], + "license": { + "PostgreSQL": "http://www.postgresql.org/about/licence" + }, + "prereqs": { + "runtime": { + "requires": { + "plpgsql": 0, + "PostgreSQL": "9.5.0" + }, + "recommends": { + "PostgreSQL": "14.0.0" + } + } + }, + "provides": { + "orafce": { + "file": "sql/orafce.sql", + "docfile": "README.orafce", + "version": "3.17.0", + "abstract": "Oracle's compatibility functions and packages" + } + }, + "resources": { + "homepage": "http://www.pgsql.cz/index.php/Oracle_functionality_%28en%29", + "repository": { + "url": "https://github.com/orafce/orafce", + "web": "https://github.com/orafce/orafce", + "type": "git" + } + }, + "generated_by": "Pavel Stehule", + "meta-spec": { + "version": "1.0.0", + "url": "http://pgxn.org/meta/spec.txt" + }, + "release_status": "stable", + "tags": [ + "oracle", + "compatibility", + "user function", + "custom function", + "intrerprocess communication", + "read from file", + "write to file", + "bussiness calendar" + ] +} diff --git a/contrib/orafce/Makefile b/contrib/orafce/Makefile new file mode 100644 index 000000000..73718ac5a --- /dev/null +++ b/contrib/orafce/Makefile @@ -0,0 +1,69 @@ +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--3.17.sql orafce--3.2--3.3.sql orafce--3.3--3.4.sql orafce--3.4--3.5.sql orafce--3.5--3.6.sql orafce--3.6--3.7.sql orafce--3.7--3.8.sql orafce--3.8--3.9.sql orafce--3.9--3.10.sql orafce--3.10--3.11.sql orafce--3.11--3.12.sql orafce--3.12--3.13.sql orafce--3.13--3.14.sql orafce--3.14--3.15.sql orafce--3.15--3.16.sql orafce--3.16--3.17.sql +DOCS = README.asciidoc COPYRIGHT.orafce INSTALL.orafce + +PG_CONFIG ?= pg_config + +# make "all" the default target +all: + +REGRESS = orafce orafce2 dbms_output dbms_utility files varchar2 nvarchar2 aggregates nlssort dbms_random regexp_func + +#REGRESS_OPTS = --load-language=plpgsql --schedule=parallel_schedule --encoding=utf8 +REGRESS_OPTS = --schedule=parallel_schedule --encoding=utf8 + +#override CFLAGS += -Wextra -Wimplicit-fallthrough=0 + +ifdef NO_PGXS +subdir = contrib/$(MODULE_big) +top_builddir = ../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +else +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +endif + +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/contrib/orafce/NEWS b/contrib/orafce/NEWS new file mode 100644 index 000000000..3a13b776b --- /dev/null +++ b/contrib/orafce/NEWS @@ -0,0 +1,72 @@ +Orafce News - History of user-visible changes +Copyright (C) 2008-2016 Orafce Global Development Group + +Version 3.14.0 - 22. Dec 2020 +* conversion function unistr +* bugfix - allows binary upgrade + +Version 3.13.4 - 31. May 2020 +* enable utl_file on MS + +Version 3.12.0 - 18. May 2020 +* trigger functions oracle.replace_empty_strings and oracle.replace_null_strings +& remove support for 9.4 + +Version 3.11.0 - 30. Mar 2020 +* named paths in utl_file package + +Version 3.9.0 - 14. Feb 2020 +* minor enhancing user_constraints view + +Version 3.8.0 - 22. May 2019 +* PostgreSQL 12 support + +Version 3.7.0 - 7. Dec 2018 +* possibility to better emulate || operator for varchar2 and nvarchar2 types +* few bugfixes +* only PostgreSQL 9.4 and newer are supported +* support for PostgreSQL 11, current master branch (future PostgreSQL 12) is supported too + +Version 3.6.0 +* some Oracle views - user_tab_columns, user_tables, user_objects, ... +* support Oracle bad used lpad and nvl functions + +Version 3.5.0 +* fix of important issue - missing IMMUTABLE flag for functions ltrim, btrim, rtrim, lpad, rpad + +Version 3.4.0 - 2017-03-14 +* new aggregate function wm_concat +* PostgreSQL 9.6, 10 are supported well + +Version 3.2.0 - 2016-01-xx +* remove support for 8.3, 8.4, 9.0, 9.1 (only 9.2 and higher are supported) +* new functions: sysdate, sessiontimezone, dbtimezone + +Version 3.1 - 2015-07-11 +* remove support for 8.2 +* add support for 9.5 +* change the releasion number system +* new functions: to_single_byte, to_multi_byte, nanvl, length, + ltrim, btrim, rtrim, lpad, rpad + +Version 3.0.10 - 1. Jan 2015 +* fix compilation issue in new code for Pg <= 9.1 + +Version 3.0.9 - 27. Dec 2014 +* new Varchar2 and Nvarchar2 types +* enhanced oracle.substr function +* fix PGXN related issues in process + +Version 3.0.7 - 27. Jul 2014 +* PostgreSQL 9.4 compilation +* new datatype and related functions: oracle.date + +Version 3.0.6 - 8. Sep 2013 + +* PostgreSQL 9.3 compilation +* some cleaning, fixes, much more regress tests + +Version 3.0.5 - X Dec 2012 + +* PostgreSQL 9.1/9.2 compilation +* ... TODO ... diff --git a/contrib/orafce/README.asciidoc b/contrib/orafce/README.asciidoc new file mode 100644 index 000000000..3bc3d2205 --- /dev/null +++ b/contrib/orafce/README.asciidoc @@ -0,0 +1,812 @@ +image::https://travis-ci.com/orafce/orafce.svg?branch=master[] + += Orafce - Oracle's compatibility functions and packages + +Functions and operators that emulate a subset of functions and packages from the Oracle RDBMS. + +There is an associated Google group - https://groups.google.com/forum/?hl=en#!forum/orafce-general + +The Orafce is supported in https://aws.amazon.com/about-aws/whats-new/2018/03/amazon-aurora-with-postgresql-compatibility-supports-minor-version-9-6-6/?nc1=h_ls[AWS Aurora with PostgreSQL Compatibility] and also in https://azure.microsoft.com/en-gb/updates/the-orafce-extension-on-azure-database-for-postgresql-is-now-available/[Azure Database for PostgreSQL]. + +== Oracle functions and Oracle packages + +This module contains some useful functions that can help with porting +Oracle application to PostgreSQL or that can be generally useful. + +Built-in Oracle date functions have been tested against Oracle 10 for +conformance. Date ranges from 1960 to 2070 work correctly. Dates before +1100-03-01 cannot be verified due to a bug in Oracle. + +All functions are fully compatibles with Oracle and respect all known +format strings. Detailed descriptions can be found on the internet. +Use keywords like : oracle round trunc date iyyy. + +== List of format strings for trunc, round functions + +---- +Y,YY,YYY,YYYY,SYYY,SYEAR year +I,IY,IYY,IYYY iso year +Q, quarter +WW week, day as first day of year +IW week, beginning Monday +W week, day as first day of month +DAY,DY,D first day of week, sunday +MONTH,MON,MM,RM month +CC,SCC century +DDD,DD,J day +HH,HH12,HH24 hour +MI minute +---- + +Functions round up. That is, a date of July 1st will be rounded to the next +year. The 16th of July will be rounded to August. + +== Date Functions + +* add_months(date, integer) date - Returns date plus n months ++ +----- + add_months(date '2005-05-31',1) -> 2005-06-30 +----- +* last_date(date) date - Returns last day of the month based on a date value ++ +---- + last_day(date '2005-05-24') -> 2005-05-31 +---- +* next_day(date, text) date - Returns the first weekday that is greater than a date value ++ +---- + next_day(date '2005-05-24', 'monday') -> 2005-05-30 +---- +* next_day(date, integer) date - Same as above. The second argument should be 1..7 and interpreted as Sunday..Satday. ++ +---- + next_day(date '2005-05-24', 1) -> 2005-05-30 +---- +* months_between(date, date) numeric - 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. ++ +---- + months_between(date '1995-02-02', date '1995-01-01') -> 1.0322580645161 +---- +* trunc(date, text) date - truncate date according to the specified format ++ +---- + trunc(date '2005-07-12', 'iw') -> 2005-07-11 +---- +* round(date, text) date - will round dates according to the specified format ++ +---- + round(date '2005-07-12', 'yyyy') -> 2006-01-01 +---- +* to_date(text) timestamp - will typecast input text to timestamp. + The GUC orafce.nls_date_format is used to specify input text format for this function. + If the value is left blank or set as DEFAULT then input text format according to + PostgreSQL's datestyle GUC setting. ++ + orafce.nls_date_format value to DEFAULT +---- + to_date('2014-05-19 17:23:53+5:30') -> 2014-05-19 17:23:53 +---- ++ + orafce.nls_date_format='YYYY-MMDD HH24:MI:SS' +---- + to_date('2014-0519 17:23:53+5:30') -> 2014-05-19 17:23:53 +---- + +== oracle.date data type + +This module contains implementation of oracle compatible DATE data type "oracle.date" and functions which are using DATE data type like oracle.add_months,oracle.last_day(),oracle.next_day(),oracle.months_between() etc. + +Example: +---- + set search_path TO oracle,"$user", public, pg_catalog; + create table oracle_date(col1 date); + insert into oracle_date values('2014-06-24 12:12:11'::date); + select * from oracle_date; + col1 + --------------------- + 2014-06-24 12:12:11 + (1 row) +---- +== oracle.date functions + +* oracle.add_months(timestamp with time zone, integer) - Returns date and time plus n months ++ +----- + oracle.add_months(oracle.date'2005-05-31 10:12:12',1) -> 2005-06-30 10:12:12 +----- +* oracle.last_day(timestamp with time zone) - Returns last day of the month based on a date value ++ +----- + oracle.last_day(oracle.date '2005-05-24 11:12:12') -> 2005-05-31 11:12:12 +----- +* oracle.next_day(timestamp with time zone, text) - Returns the first weekday that is greater than a date value ++ +----- + oracle.next_day(oracle.date '2005-05-24 10:12:12', 'monday') -> 2005-05-30 10:12:12 +----- +* oracle.next_day(timestamp with time zone, integer) - Same as above. The second argument should be 1..7 and interpreted as Sunday..Saturday. ++ +----- + oracle.next_day(oracle.date '2005-05-24 11:21:12', 1) -> 2005-05-29 11:21:12 +----- +* oracle.months_between(timestamp with time zone, timestamp with time zone) - Returns the number of months between timestamp1 and timestamp2. If a fractional month is calculated, the months_between function calculates the fraction based on a 31-day month. ++ +----- + oracle.months_between(oracle.date '1995-02-02 10:00:00', oracle.date '1995-01-01 10:21:11') -> 1.03225806451613 +----- +* oracle.to_date(text,text) - Returns timestamp without time zone. ++ +---- + oracle.to_date('02/16/09 04:12:12', 'MM/DD/YY HH24:MI:SS') -> 2009-02-16 04:12:12 +---- +* oracle.to_date(text) - Returns oracle.date ++ +---- + oracle.to_date('02/16/09 04:12:12') -> 2009-02-16 04:12:12 +---- +* oracle.sysdate() - Returns statement timestamp at server timezone (orafce.timezone) ++ +----- + oracle.sysdate() -> 2015-12-09 17:47:56 +----- +* oracle.dbtimezone - Returns server time zone - emulated via orafce.timezone ++ +----- + oracle.dbtimezone() -> GMT +----- +* oracle.sessiontimezone() - Returns session timezone - current PostgreSQL timezone ++ +----- + oracle.sessiontimezone() -> Europe/Prague +----- +* oracle.to_char(timestamp) - Returns timestamp in nls_date_format. ++ +---- + orafce.nls_date_format='YY-MonDD HH24:MI:SS' +---- ++ +---- + oracle.to_char(to_date('14-Jan08 11:44:49+05:30')) -> 14-Jan08 11:44:49 +---- ++ +---- + orafce.nls_date_format='YY-MonDD HH24:MI:SS' +---- ++ +---- + oracle.to_char(oracle.to_date('21052014 12:13:44+05:30','DDMMYYYY HH24:MI:SS')) -> 14-May21 12:13:44 +---- + + + +== oracle.date Operators + +* oracle.+(oracle.date,smallint) - Returns oracle.date ++ +---- + oracle.to_date('2014-07-02 10:08:55','YYYY-MM-DD HH:MI:SS') + 9::smallint -> 2014-07-11 10:08:55 +---- +* oracle.+(oracle.date,integer) - Returns oracle.date ++ +---- + oracle.to_date('2014-07-02 10:08:55','YYYY-MM-DD HH:MI:SS') + 9::integer -> 2014-07-11 10:08:55 +---- +* oracle.+(oracle.date,bigint) - Returns oracle.date ++ +---- + oracle.to_date('2014-07-02 10:08:55','YYYY-MM-DD HH:MI:SS') + 9::bigint -> 2014-07-11 10:08:55 +---- +* oracle.+(oracle.date,numeric) - Returns oracle.date ++ +---- + oracle.to_date('2014-07-02 10:08:55','YYYY-MM-DD HH:MI:SS') + 9::numeric -> 2014-07-11 10:08:55 +---- +* oracle.-(oracle.date,smallint) - Returns oracle.date ++ +---- + oracle.to_date('2014-07-02 10:08:55','YYYY-MM-DD HH:MI:SS') - 9::smallint -> 2014-06-23 10:08:55 +---- +* oracle.-(oracle.date,integer) - Returns oracle.date ++ +---- + oracle.to_date('2014-07-02 10:08:55','YYYY-MM-DD HH:MI:SS') - 9::integer -> 2014-06-23 10:08:55 +---- +* oracle.-(oracle.date,bigint) - Returns oracle.date ++ +---- + oracle.to_date('2014-07-02 10:08:55','YYYY-MM-DD HH:MI:SS') - 9::bigint -> 2014-06-23 10:08:55 +---- +* oracle.-(oracle.date,numeric) - Returns oracle.date ++ +---- + oracle.to_date('2014-07-02 10:08:55','YYYY-MM-DD HH:MI:SS') - 9::numeric -> 2014-06-23 10:08:55 +---- +* oracle.-(oracle.date,oracle.date) - Returns double precision ++ +---- + oracle.to_date('2014-07-17 11:10:15', 'yyyy-mm-dd hh24:mi:ss') - oracle.to_date('2014-02-01 10:00:00', 'yyyy-mm-dd hh24:mi:ss') -> 166.048785 +---- + +You need to set search_path TO oracle,"$user", public, pg_catalog +because functions like oracle.add_months,oracle.last_day,oracle.next_day,oracle.months_between are installed side-by-side with pg_catalog.add_months,pg_catalog.last_day,pg_catalog.next_day,pg_catalog.months_between. + +== Table dual + +PostgreSQL does not need Oracle's table 'dual', but since it is intensively +used by Oracle users, it has been added in orafce. + +== Package dbms_output + +PostgreSQL sends information to the client via RAISE NOTICE. Oracle uses +dbms_output.put_line(). This works differently from RAISE NOTICE. Oracle has +a session queue, put_line() adds a line to the queue and the function +get_line() reads from queue. If flag 'serveroutput' is set, then client +over all sql statements reads queue. You can use: + +---- + select dbms_output.enable(); + select dbms_output.put_line('first_line'); + select dbms_output.put_line('next_line'); + select * from dbms_output.get_lines(0); +---- + +or + +---- + select dbms_output.enable(); + select dbms_output.serveroutput('t'); + select dbms_output.put_line('first_line'); +---- + +This package contains the following functions: enable(), disable(), +serveroutput(), put(), put_line(), new_line(), get_line(), get_lines(). +The package queue is implemented in the session's local memory. + +== Package utl_file + +This package allows PL/pgSQL programs to read from and write to any files that are +accessible from server. Every session can open a maximum of ten files and max +line size is 32K. This package contains following functions: + +* utl_file.fclose(file utl_file.file_type) - close file +* utl_file.fclose_all() - close all files +* utl_file.fcopy(src_location, src_filename, dest_location, dest_filename[, start_line][, end_line]) - copy text file +* utl_file.fflush(file utl_file.file_type) - flushes all data from buffers +* utl_file.fgetattr(location, filename) - get file attributes +* utl_file.fopen(location text, filename text, file_mode text [, maxlinesize int] [, encoding name]) utl_file.file_type - open file +* utl_file.fremove(location, filename) - remove file +* utl_file.frename(location, filename, dest_dir, dest_file[, overwrite]) - rename file +* utl_file.get_line(file utl_file.file_type) text - read one line from file +* utl_file.get_nextline(file utl_file.file_type) text - read one line from file or returns NULL +* utl_file.is_open(file utl_file.file_type) bool - returns true, if file is opened +* utl_file.new_line(file utl_file.file_type [,rows int]) - puts some new line chars to file +* utl_file.put(file utl_file.file_type, buffer text) - puts buffer to file +* utl_file.put_line(file utl_file.file_type, buffer text) - puts line to file +* utl_file.putf(file utl_file.file_type, format buffer [,arg1 text][,arg2 text][..][,arg5 text]) - put formated text into file +* utl_file.tmpdir() - get path of temp directory + +Because PostgreSQL doesn't support call by reference, some functions are slightly different: +fclose and get_line. + +---- + declare f utl_file.file_type; + begin + f := utl_file.fopen('/tmp', 'sample.txt', 'r'); + <> + loop + begin + raise notice '%', utl_file.get_line(f); + exception + when no_data_found then + exit readl; + end; + end loop; + f := fclose(f); + end; +---- + +or second (with PostgreSQL specific function get_nextline) + +---- + declare + f utl_file.file_type; + line text; + begin + f := utl_file.fopen('/tmp', 'sample.txt', 'r'); + loop + line := utl_file.get_nextline(f); + exit when line is NULL; + raise notice '%', line; + exception + when others then + utl_file.fclose_all(); + end; +---- + +Before using the package you have to set the utl_file.utl_file_dir table. +It contains all allowed directories without ending symbol ('/' or '\'). +On WinNT platform, the paths have to end with symbol '\' everytime. + +Directory entries can be named (second column in table `utl_file.utl_file_dir`). +The `location` parameter can be either the directory name or the dictionary path. +The location is first interpreted and checked as a directory name. +If not found (in 2nd column), then the location is interpreted and checked as a path. + +Functions from utl_file package (schema on Postgres) requires a access to +table utl_file.utl_file_dir. This fact can be used to control what users +can use these functions or not. Default setting is READ for PUBLIC. INSERT, UPDATE can +do only privileged user (super user). So unprivileged user can use functions +from this package, but cannot to change list of safe directories (content of +utl_file.utl_file_dir table). The content of this table is visible for PUBLIC +(or should be visible for users who uses functions from this package). + +== Package dbms_pipe + +This package is an emulation of dbms_pipe Oracle package. It provides +inter-session comunication. You can send and read any message with or without +waiting; list active pipes; set a pipe as private or public; and, use +explicit or implicit pipes. + +The maximum number of pipes is 50. + +Shared memory is used to send messages. + +An example follows: + +---- +-- Session A +select dbms_pipe.create_pipe('my_pipe',10,true); -- explicit pipe creating +select dbms_pipe.pack_message('neco je jinak'); +select dbms_pipe.pack_message('anything is else'); +select dbms_pipe.send_message('my_pipe',20,0); -- change limit and send without waiting +select * from dbms_pipe.db_pipes; -- list of current pipes + +-- Session B +select dbms_pipe.receive_message('my_pipe',1); -- wait max 1 sec for message +select dbms_pipe.next_item_type(); -- -> 11, text +select dbms_pipe.unpack_message_text(); +select dbms_pipe.next_item_type(); -- -> 11, text +select dbms_pipe.unpack_message_text(); +select dbms_pipe.next_item_type(); -- -> 0, no more items +select dbms_pipe.remove_pipe('my_pipe'); +---- + +There are some differences compared to Oracle, however: + +* limit for pipes isn't in bytes but in elements in pipe +* you can send message without waiting +* you can send empty messages +* next_item_type knows about TIMESTAMP (type 13) +* PostgreSQL doesn't know about the RAW type, use bytea instead + +== Package dbms_alert + +Another means of inter-process communication. + +---- +-- Session A +select dbms_alert.register('boo'); +select * from dbms_alert.waitany(10); + +-- Session B +select dbms_alert.register('boo'); +select * from dbms_alert.waitany(10); + +-- Session C +select dbms_alert.signal('boo','Nice day'); +---- + +== Package PLVdate + +This module contains some functions for working with business days from +package PLVdate. Detailed documentation can be found in PLVision library. +This package is multicultural, but default configurations are only for +european countries (see source code). + +You should define your own non-business days (max 50 days) and own +holidays (max 30 days). A holiday is any non-business day, which is the same +every year. For example, Christmas day in Western countries. + +=== Functions + +* plvdate.add_bizdays(day date, days int) date - Get the date created by adding business days to a date +* plvdate.nearest_bizday(day date) date - Get the nearest business date to a given date, user defined +* plvdate.next_bizday(day date) date - Get the next business date from a given date, user defined +* plvdate.bizdays_between(day1 date, day2 date) int - Get the number of business days between two dates +* plvdate.prev_bizday(day date) date - Get the previous business date from a given date +* plvdate_isbizday(date) bool - Call this function to determine if a date is a business day +* plvdate.set_nonbizday(dow varchar) - Set day of week as non bussines day +* plvdate.unset_nonbizday(dow varchar) - Unset day of week as non bussines day +* plvdate.set_nonbizday(day date) - Set day as non bussines day +* plvdate.unset_nonbizday(day date) - Unset day as non bussines day +* plvdate.set_nonbizday(day date, repeat bool) - Set day as non bussines day, if 'repeat' is true, then day is nonbiz every year +* plvdate.unset_nonbizday(day date, repeat bool) - Unset day as non bussines day, if 'repeat' is true, then day is nonbiz every year +* plvdate.use_easter() - Easter Sunday and easter monday will be holiday +* plvdate.unuse_easter(); +* plvdate.use_easter(useit boolean); +* plvdate.using_easter() bool - If we use easter then returns true +* plvdate.use_great_friday() - Easter Great Friday will be holiday +* plvdate.unuse_easter(); +* plvdate.use_easter(useit boolean); +* plvdate.using_easter() bool - If we use easter Great Friday as holiday then returns true +* plvdate.include_start() - Include starting date in bizdays_between calculation +* plvdate.noinclude_start(); +* plvdate.include_start(include boolean); +* plvdate.including_start() bool; +* plvdate.default_holidays(varchar) - load default configurations. You can use the following configurations: + Czech, German, Austria, Poland, Slovakia, Russia, GB and USA at this moment. +* configuration contains only common holidays for all regions. You can add your own regional holiday with plvdate.set_nonbizday(nonbizday, true) + + +Example: + +---- +postgres=# select plvdate.default_holidays('czech'); + default_holidays + ----------------- + +(1 row) +postgres=# select to_char(current_date, 'day'), + plvdate.next_bizday(current_date), + to_char(plvdate.next_bizday(current_date),'day'); + to_char | next_bizday | to_char + ----------+-------------+----------- + saturday | 2006-03-13 | monday +(1 row) +---- + +Change for non-European environment: + +---- +select plvdate.unset_nonbizday('saturday'); +select plvdate.unset_nonbizday('sunday'); +select plvdate.set_nonbizday('friday'); +select plvdate.set_nonbizday('2006-05-19', true); +select plvdate.unuse_easter(); +---- + +== Package PLVstr and PLVchr + +This package contains some useful string and character functions. Each +function supports positive and negative offsets -- i.e., offset from the +end of the string. For example: + +---- +plvstr.left('abcdef',2) -> ab +plvstr.left('abcdef',-2) -> abcd +plvstr.substr('abcdef',1,1) -> a +plvstr.substr('abcdef',-1,1) -> f +plvstr.substr('abcde',-2,1) -> d +---- + +List of functions: + +* plvstr.normalize(str text) - Normalize string - Replace white chars by space, replace spaces by space +* plvstr.is_prefix(str text, prefix text, cs bool) - Returns true, if prefix is prefix of str +* plvstr.is_prefix(str text, prefix text) - Returns true, if prefix is prefix of str +* plvstr.is_prefix(str int, prefix int) - Returns true, if prefix is prefix of str +* plvstr.is_prefix(str bigint, prefix bigint) - Returns true, if prefix is prefix of str +* plvstr.substr(str text, start int, len int) - Returns substring started on start_in to end +* plvstr.substr(str text, start int) - Returns substring started on start_in to end +* plvstr.instr(str text, patt text, start int, nth int) - Search pattern in string +* plvstr.instr(str text, patt text, start int) - Search pattern in string +* plvstr.instr(str text, patt text) - Search pattern in string +* plvstr.lpart(str text, div text, start int, nth int, all_if_notfound bool) - Call this function to return the left part of a string +* plvstr.lpart(str text, div text, start int, nth int) - Call this function to return the left part of a string +* plvstr.lpart(str text, div text, start int) - Call this function to return the left part of a string +* plvstr.lpart(str text, div text) - Call this function to return the left part of a string +* plvstr.rpart(str text, div text, start int, nth int, all_if_notfound bool) - Call this function to return the right part of a string +* plvstr.rpart(str text, div text, start int, nth int) - Call this function to return the right part of a string +* plvstr.rpart(str text, div text, start int) - Call this function to return the right part of a string +* plvstr.rpart(str text, div text) - Call this function to return the right part of a string +* plvstr.lstrip(str text, substr text, num int) - Call this function to remove characters from the beginning +* plvstr.lstrip(str text, substr text) - Call this function to remove characters from the beginning +* plvstr.rstrip(str text, substr text, num int) - Call this function to remove characters from the end +* plvstr.rstrip(str text, substr text) - Call this function to remove characters from the end +* plvstr.rvrs(str text, start int, _end int) - Reverse string or part of string +* plvstr.rvrs(str text, start int) - Reverse string or part of string +* plvstr.rvrs(str text) - Reverse string or part of string +* plvstr.left(str text, n int) - Returns firs num_in charaters. You can use negative num_in +* plvstr.right(str text, n int) - Returns last num_in charaters. You can use negative num_ni +* plvstr.swap(str text, replace text, start int, lengh int) - Replace a substring in a string with a specified string +* plvstr.swap(str text, replace text) - Replace a substring in a string with a specified string +* plvstr.betwn(str text, start int, _end int, inclusive bool) - Find the Substring Between Start and End Locations +* plvstr.betwn(str text, start text, _end text, startnth int, endnth int, inclusive bool, gotoend bool) - Find the Substring Between Start and End Locations +* plvstr.betwn(str text, start text, _end text) - Find the Substring Between Start and End Locations +* plvstr.betwn(str text, start text, _end text, startnth int, endnth int) - Find the Substring Between Start and End Locations +* plvchr.nth(str text, n int) - Call this function to return the Nth character in a string +* plvchr.first(str text) - Call this function to return the first character in a string +* plvchr.last(str text) - Call this function to return the last character in a string +* plvchr.is_blank(c int) - Is blank +* plvchr.is_blank(c text) - Is blank +* plvchr.is_digit(c int) - Is digit +* plvchr.is_digit(c text) - Is digit +* plvchr.is_quote(c int) - Is quote +* plvchr.is_quote(c text) - Is quote +* plvchr.is_other(c int) - Is other +* plvchr.is_other(c text) - Is other +* plvchr.is_letter(c int) - Is letter +* plvchr.is_letter(c text) - Is letter +* plvchr.char_name(c text) - Returns the name of the character to ascii code as a VARCHAR. +* plvchr.quoted1(str text) - Quoted text between ''' +* plvchr.quoted2(str text) - Quoted text between '"' +* plvchr.stripped(str text, char_in text) - Strips a string of all instances of the specified characters + + +== Package PLVsubst + +The PLVsubst package performs string substitutions based on a substitution keyword. + +* plvsubst.string(template_in text, vals_in text[]) - Scans a string for all instances of the substitution keyword and replace it with the next value in the substitution values list +* plvsubst.string(template_in text, vals_in text[], subst_in text) +* plvsubst.string(template_in text, vals_in text, delim_in text) +* plvsubst.string(template_in text, vals_in text, delim_in text, subst_in text) +* plvsubst.setsubst(str text) - Set substitution keyword to default '%s' +* plvsubst.subst() - Retrieve substitution keyword + +Examples: + +---- +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 %s %s.', 'Pavel,Stěhule'); + string + -------------------------- + My name is Pavel Stěhule. +(1 row) + +select plvsubst.string('My name is $$ $$.', 'Pavel|Stěhule','|','$$'); + string + -------------------------- + My name is Pavel Stěhule. +(1 row) +---- + + +== Package DBMS_utility + +* dms_utility.format_call_stack() -- return a formatted string with content of call stack + +---- +postgres=# select foo2(); + foo2 + --------------------------------- + ----- Call Stack ----- + line object + number statement name + 1 return function foo + 1 return function foo1 + 1 return function foo2 +(1 row) +---- + + +== Package PLVlex + +This package isn't compatible with original PLVlex. + +---- +postgres=# select * from + plvlex.tokens('select * from a.b.c join d ON x=y', true, true); + + pos | token | code | class | separator | mod + ----+--------+------+---------+-----------+------ + 0 | select | 527 | KEYWORD | | + 7 | * | 42 | OTHERS | | self + 9 | from | 377 | KEYWORD | | + 25 | a.b.c | | IDENT | | + 20 | join | 418 | KEYWORD | | + 25 | d | | IDENT | | + 27 | on | 473 | KEYWORD | | + 30 | x | | IDENT | | + 31 | = | 61 | OTHERS | | self + 32 | y | | IDENT | | +(10 rows) +---- + +Warning: Keyword's codes can be changed between PostgreSQL versions! +o plvlex.tokens(str text, skip_spaces bool, qualified_names bool) - Returns table of lexical elements in str. + +== DBMS_SQL +In this time this package is in independent project https://github.com/okbob/dbms_sql . It introduces a procedures - that are supported by PostgreSQL 11. Orafce runs on PostgreSQL 9.5, so it is not possible to merge DBMS_SQL into Orafce now. If you have PostgreSQL 11 and higher, then you can use DBMS_SQL and Orafce together. + +== DBMS_ASSERT + +This package protects user input against SQL injection. + +* dbms_assert.enquote_literal(varchar) varchar - Add leading and trailing quotes, verify that all single quotes are paired with adjacent single quotes. +* dbms_assert.enquote_name(varchar [, boolean]) varchar - Enclose name in double quotes. Optional second parameter ensure loweralize of name. Attention - On Oracle is second parameter capitalize! +* dbms_assert.noop(varchar) varchar - Returns value without any checking. +* dbms_assert.qualified_sql_name(varchar) varchar - This function verifies that the input string is qualified SQL name. +* dbms_assert.schema_name(varchar) varchar - Function verifies that input string is an existing schema name. +* dbms_assert.simple_sql_name(varchar) varchar -This function verifies that the input string is simple SQL name. +* dbms_assert.object_name(varchar) varchar - Verifies that input string is qualified SQL identifier of an existing SQL object. + +== PLUnit + +This unit contains some assert functions. + +* plunit.assert_true(bool [, varchar]) - Asserts that the condition is true. +* plunit.assert_false(bool [, varchar]) - Asserts that the condition is false. +* plunit.assert_null(anyelement [, varchar]) - Asserts that the actual is null. +* plunit.assert_not_null(anyelement [, varchar]) - Asserts that the actual isn't null. +* plunit.assert_equals(anyelement, anyelement [, double precision] [, varchar]) - Asserts that expected and actual are equal. +* plunit.assert_not_equals(anyelement, anyelement [, double precision] [, varchar]) - Asserts that expected and actual are equal. +* plunit.fail([varchar]) - Fail can be used to cause a test procedure to fail immediately using the supplied message. + +== Package DBMS_random + +* dbms_random.initialize(int) - Initialize package with a seed value. +* dbms_random.normal() - Returns random numbers in a standard normal distribution. +* dbms_random.random() - Returns random number from -2^31 .. 2^31. +* dbms_random.seed(int) +* dbms_random.seed(text) - Reset seed value. +* dbms_random.string(opt text(1), len int) - Create random string +* dbms_random.terminate() - Terminate package (do nothing in Pg) +* dbms_random.value() - Returns a random number from [0.0 - 1.0) +* dbms_random.value(low double precision, high double precision) - Returns a random number from [low - high) + +== Others functions + +This module contains implementation of functions: concat, nvl, nvl2, lnnvl, decode, +bitand, nanvl, sinh, cosh, tanh and oracle.substr. + +* oracle.substr(str text, start int, len int) - Oracle compatible substring +* oracle.substr(str text, start int) - Oracle compatible substring +* oracle.substr(str numeric, start numeric) - Oracle compatible substring +* oracle.substr(str numeric, start numeric, len numeric) - Oracle compatible substring +* oracle.substr(str varchar, start numeric) - Oracle compatible substring +* oracle.substr(str varchar, start numeric,len numeric) - Oracle compatible substring +* oracle.lpad(string, length [, fill]) - Oracle compatible lpad +* oracle.rpad(string, length [, fill]) - Oracle compatible rpad +* oracle.ltrim(string text [, characters text]) - Oracle compatible ltrim +* oracle.rtrim(string text [, characters text]) - Oracle compatible rtrim +* oracle.btrim(string text [, characters text]) - Oracle compatible btrim +* oracle.length(string char) - Oracle compatible length +* pg_catalog.listagg(str text [, separator text]) - aggregate values to list +* pg_catalog.wm_concat(str text) - aggregate values to comma separatated list +* pg_catalog.median(float4) - calculate a median +* pg_catalog.median(float8) - calculate a median +* pg_catalog.to_number(text) - converts a string to a number +* pg_catalog.to_number(numeric) - converts a string to a number +* pg_catalog.to_number(numeric,numeric) - converts a string to a number +* public.to_multi_byte(text) - Convert all single-byte characters to their corresponding multibyte characters +* public.to_single_byte(text) - Convert all multi-byte characters to their corresponding single-byte characters + +You might need to set search_path to 'oracle, pg_catalog, "$user", public' +because oracle.substr, oracle.lpad, oracle.rpad, oracle.ltrim, oracle.rtrim, oracle.btrim, oracle.length are installed side-by-side with pg_catalog.substr, pg_catalog.lpad, pg_catalog.rpad, pg_catalog.ltrim, pg_catalog.rtrim, pg_catalog.btrim, pg_catalog.length respectively. + +Note that in case of lpad and rpad, parameters string and fill can be of types CHAR, VARCHAR, TEXT, VARCHAR2 or NVARCHAR2 (note that the last two are orafce-provided types). The default fill character is a half-width space. Similarly for ltrim, rtrim and btrim. + +Note that oracle.length has a limitation that it works only in units of characters because PostgreSQL CHAR type only supports character semantics. + +== VARCHAR2 and NVARCHAR2 Support + +orafce's VARCHAR2 implements parts of Oracle database specification about VARCHAR2: + +* Unit of type modifier = 'bytes' (for character semantics, see NVARCHAR2) +* Unlike PostgreSQL varchar, implicit cast to VARCHAR2 does not truncate + white spaces over declared maximum length +* For these types is possible to use null safe || operator, when you enable + orafce.varchar2_null_safe_concat TO true . The behave is very similar to Oracle. + + Attention: - when result is empty string, then result is NULL. This behaviour is + disabled by default. + + Attention: - there is possible incompatibility between 3.7 and older Orafce + releases. A operator function is now marked as stable (was immutable before). + It's not possible to create functional indexes over stable or volatile expressions. + +---- +-- null safe concat (disabled by default) +SELECT NULL || 'hello'::varchar2 || NULL; + +SET orafce.varchar2_null_safe_concat TO true; +SELECT NULL || 'hello'::varchar2 || NULL; +---- + +Please note that PostgreSQL does not allow to dynamically specify how we +interpret varchar strings. It always interprets them as 'character' strings +as determined by database encoding. So, we cannot support both BYTE and +CHARACTER semantics for a given varchar type in the same database. We chose +to implement the BYTE semantics as that is default in Oracle. For CHARACTER +semantics, please see NVARCHAR2 which by default always implements the +CHARACTER semantics. + +Please be careful when using the above type to store strings consisting of +multibyte encoded characters wherein each character may be composed of an +arbitrary number of bytes. + +NVARCHAR2 implements the following: + +* Unit of type modifier = 'characters' (using the character set/encoding of the database) + +Use this type if character semantics is preferred. + +Please note that unlike Oracle, orafce's VARCHAR2 and NVARCHAR2 do not impose the 4000 bytes limit on the 'declared' size. +In fact it is same as that of PostgreSQL varchar, which is about 10MB (although varchar can theoretically store values of size up to 1GB) + +Some byte-based string functions to be used with VARCHAR2 strings + +* substrb(VARCHAR2, int [, int]) - extract a substring of specified length (in bytes) starting at a given byte position (counting from one); if the third argument isnot specified then length to the end of the string is considered +* strposb(VARCHAR2, VARCHAR2) - returns the location of specified substring in a given string (counting from one) +* lengthb(VARCHAR2) - returns the length (in bytes) of a given string + +== Triggers == + +Oracle doesn't make differences between NULL and empty string (when a value +is used as text). For Postgres NULL and empty string are different values. +For simplicity is good to ensure (in Postgres database) use only NULLs (and +don't use empty strings) or use only empty strings (and don't use NULLs) for +text type columns. Both variants has some advantages and disadvantages. + +This can be enusured with trigger functions: + +---- +oracle.replace_empty_strings([raise_warnings boolean]) +oracle.replace_null_strings([raise_warnings boolean]) +---- + +Optional boolean argument is used as indicator so these functions should to +raise warning when row was changed inside these functions. + +---- +CREATE TABLE test(id serial, name varchar, surname varchar); +CREATE TRIGGER test_trg + BEFORE INSERT OR UPDATE + ON test + FOR EACH ROW + EXECUTE PROCEDURE oracle.replace_empty_strings(); + +INSERT INTO test(name, surname) VALUES('', 'Stehule'); + +-- name will be replaced by NULL +---- + +== Emulated views + +* oracle.user_tab_columns +* oracle.user_tables +* oracle.user_cons_columns +* oracle.user_constraints +* oracle.product_componenent_version +* oracle.user_objects +* oracle.dba_segments + +== TODO + +* better documentation +* better seralization in dbms_pipe (via _send and _recv functions) +* alter shared memory structures by temporary tables: only locks are in shmem, (bitmaps), data in tmp tbl + +== License + +This module is released under BSD licence. + +== Contributors + +The project was founded in 2008 by Pavel Stehule . + +Other contributors: + +* Gabriele Bartolini (gbartolini) +* Jeffrey Cohen (jcohen) +* Giles Darold (darold) +* Pavan Deolasee (pavanvd) +* Peter Eisentraut (petere) +* Beena Emerson (b-emerson) +* Takahiro Itagaki (itagaki) +* Zdenek Kotala (hlipa) +* Amit Langote (amitlan) +* Heikki Linnakangas (hlinnaka) +* Fujii Masao +* Marco Nenciarini (mnencia) +* Vinayak Pokale +* Gavin Sherry (swm) +* Pavel Stehule (okbob) +* Rahila Syed (rahila) diff --git a/contrib/orafce/README.msvc b/contrib/orafce/README.msvc new file mode 100644 index 000000000..cea8c96a8 --- /dev/null +++ b/contrib/orafce/README.msvc @@ -0,0 +1,51 @@ +Build on Microsoft Windows with Microsoft Visual Studio + +1. For Postgres 12 and higher you need libicu headers + +2. Orafce library is linked with runtime library - this library should to use + same platform toolset like Postgres server. + +3. Start empty project + +4. Add existing items - *.c and *.h files except "sqlscan.c". This file is compiled + as include of sqlparse.c and should not be compiled as separate c file! + +5. In configuration manager choose configuration - "Release" and platform. Platform must be + same like your Postgres server. + +6. V project properties set + + Under "Configuration Properties" -> "General", set "Configuration Type" to "Dynamic Library (.dll)". + + Under "C/C++" -> "Preprocessor Directives", add the directive "WIN32" + + Under "C/C++" -> "Code Generation", set "Enable C++ Exceptions" to "No" + "C/C++" -> "Code generation" -> "Runtime library" must be /MD + + Under "Advanced" set "Compile As" to "C Code" + + Under "Linker" -> "Manifest File", set "Generate Manifest" to "No" + + Under "Linker" -> "Input" -> "Additional Dependencies", add postgres.lib to the library list + + In the properties dialog, go to "Configuration Properties" -> "C/C++" -> "General", "Additional + Include Directories" + + include\server\port\win32_msvc + include\server\port\win32 + include\server + include + + Attention, there should be real paths to include files .. "include\server\port\win32_msvc" is in my case + "C:\Program Files\PostgreSQL\13\include\server\port\win32_msvc". All four paths to include folders should + be correct. + + PostgreSQL 10 and higher requires lib ICU include file. I had install libICU before, and then add to + "Additional Include Directories" C:\Users\user\Documents\icu-59.1-vs2015\include + + You’ll also need to set the library path. Under "Linker"->"General", in Additional Library Directories. + In my case that’s C:\Program Files\PostgreSQL\12\lib. + + set "Linker"->"General"->"Link Library Dependencies" to No. + +7. make build diff --git a/contrib/orafce/aggregate.c b/contrib/orafce/aggregate.c new file mode 100644 index 000000000..beb2a3cc5 --- /dev/null +++ b/contrib/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/contrib/orafce/alert.c b/contrib/orafce/alert.c new file mode 100644 index 000000000..f89542993 --- /dev/null +++ b/contrib/orafce/alert.c @@ -0,0 +1,1081 @@ +#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 "storage/procarray.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) + +static void unregister_event(int event_id, int sid); +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); + + + +/* + * 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; +} + +/* + * this function is called when we know so session with sid is not valid + * anymore. + */ +static void +purge_shared_alert_mem() +{ + int i; + +#if PG_VERSION_NUM >= 90600 + + LWLockAcquire(ProcArrayLock, LW_SHARED); + +#endif + + for (i = 0; i < MAX_LOCKS; i++) + { + PGPROC *proc; + + if (locks[i].sid == NOT_USED) + continue; + +#if PG_VERSION_NUM < 90600 + + proc = BackendPidGetProc(locks[i].pid); + +#else + + proc = BackendPidGetProcWithLock(locks[i].pid); + +#endif + + if (proc == NULL) + { + int j; + int invalid_sid = locks[i].sid; + + for (j = 0; j < MAX_EVENTS; j++) + { + if (events[j].event_name != NULL) + { + find_and_remove_message_item(j, invalid_sid, + false, true, true, NULL, NULL); + unregister_event(j, invalid_sid); + } + } + + locks[i].sid = NOT_USED; + } + } + +#if PG_VERSION_NUM >= 90600 + + LWLockRelease(ProcArrayLock); + +#endif + +} + +/* + * 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) + { + purge_shared_alert_mem(); + + for (i = 0; i < MAX_LOCKS; i++) + { + if (locks[i].sid == NOT_USED) + { + first_free = i; + break; + } + } + } + + if (first_free != NOT_FOUND) + { + locks[first_free].sid = sid; + locks[first_free].echo = NULL; + locks[first_free].pid = MyProcPid; + 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; + alert_lock *alck; + + 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); + } + } + + alck = find_lock(sid, false); + if (alck) + { + /* After all events unregistration, an echo field should NULL */ + Assert(alck->echo == NULL); + + alck->sid = NOT_USED; + session_lock = NULL; + } + + 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/contrib/orafce/assert.c b/contrib/orafce/assert.c new file mode 100644 index 000000000..f011437fa --- /dev/null +++ b/contrib/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/contrib/orafce/assert.h b/contrib/orafce/assert.h new file mode 100644 index 000000000..45c2fc7f1 --- /dev/null +++ b/contrib/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/contrib/orafce/builtins.h b/contrib/orafce/builtins.h new file mode 100644 index 000000000..9ca757ad4 --- /dev/null +++ b/contrib/orafce/builtins.h @@ -0,0 +1,295 @@ + +#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); +extern PGDLLEXPORT Datum dbms_utility_get_time(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/contrib/orafce/charlen.c b/contrib/orafce/charlen.c new file mode 100644 index 000000000..3e71afae5 --- /dev/null +++ b/contrib/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/contrib/orafce/charpad.c b/contrib/orafce/charpad.c new file mode 100644 index 000000000..5ea86a04e --- /dev/null +++ b/contrib/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/contrib/orafce/convert.c b/contrib/orafce/convert.c new file mode 100644 index 000000000..abf31a41b --- /dev/null +++ b/contrib/orafce/convert.c @@ -0,0 +1,933 @@ +#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 UTF8 and %s is not supported", + 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"))); + +#if defined(_MSC_VER) + + /* be MSVC quiet */ + return 0; + +#endif +} diff --git a/contrib/orafce/datefce.c b/contrib/orafce/datefce.c new file mode 100644 index 000000000..6ec781a5a --- /dev/null +++ b/contrib/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/contrib/orafce/doc/orafce_documentation/Orafce_Documentation_01.md b/contrib/orafce/doc/orafce_documentation/Orafce_Documentation_01.md new file mode 100644 index 000000000..0f5520ce3 --- /dev/null +++ b/contrib/orafce/doc/orafce_documentation/Orafce_Documentation_01.md @@ -0,0 +1,142 @@ +Orafce Documentation +=== + +Orafce - Oracle's compatibility functions and packages +--- + +This documentation describes the environment settings and functionality offered for features that are compatible with Oracle databases. + + +Chapter 1 Overview +--- + +Features compatible with Oracle databases are provided. +These features enable you to easily migrate to PostgreSQL and reduce the costs of reconfiguring applications. + +The table below lists features compatible with Oracle databases. + + + +### 1.1 Features compatible with Oracle databases + +**Data type** + +|Item|Overview| +|:---|:---| +|VARCHAR2|Variable-length character data type| +|NVARCHAR2|Variable-length national character data type| +|DATE|Data type that stores date and time| + +**SQL Queries** + +|Item|Overview| +|:---|:---| +|DUAL table|Table provided by the system| + + +**SQL Functions** + + - Mathematical functions + +|Item|Overview| +|:---|:---| +|BITAND|Performs a bitwise AND operation| +|COSH|Calculates the hyperbolic cosine of a number| +|SINH|Calculates the hyperbolic sine of a number| +|TANH|Calculates the hyperbolic tangent of a number| + + + - String functions + +|Item|Overview| +|:---|:---| +|INSTR|Returns the position of a substring in a string| +|LENGTH|Returns the length of a string in number of characters| +|LENGTHB|Returns the length of a string in number of bytes| +|LPAD|Left-pads a string to a specified length with a sequence of characters| +|LTRIM|Removes the specified characters from the beginning of a string| +|NLSSORT|Returns a byte string used to sort strings in linguistic sort sequence based on locale| +|REGEXP_COUNT|searches a string for a regular expression, and returns a count of the matches| +|REGEXP_INSTR|returns the beginning or ending position within the string where the match for a pattern was located| +|REGEXP_LIKE|condition in the WHERE clause of a query, causing the query to return rows that match the given pattern| +|REGEXP_SUBSTR|returns the string that matches the pattern specified in the call to the function| +|REGEXP_REPLACE|replace substring(s) matching a POSIX regular expression| +|RPAD|Right-pads a string to a specified length with a sequence of characters| +|RTRIM|Removes the specified characters from the end of a string| +|SUBSTR|Extracts part of a string using characters to specify position and length| +|SUBSTRB|Extracts part of a string using bytes to specify position and length| + + + - Date/time functions + +|Item|Overview| +|:---|:---| +|ADD_MONTHS|Adds months to a date| +|DBTIMEZONE|Returns the value of the database time zone| +|LAST_DAY|Returns the last day of the month in which the specified date falls| +|MONTHS_BETWEEN|Returns the number of months between two dates| +|NEXT_DAY|Returns the date of the first instance of a particular day of the week that follows the specified date| +|ROUND|Rounds a date| +|SESSIONTIMEZONE|Returns the time zone of the session| +|SYSDATE|Returns the system date| +|TRUNC|Truncates a date| + + + - Data type formatting functions + +|Item|Overview| +|:---|:---| +|TO_CHAR|Converts a value to a string| +|TO_DATE|Converts a string to a date in accordance with the specified format| +|TO_MULTI_BYTE|Converts a single-byte string to a multibyte string| +|TO_NUMBER|Converts a value to a number in accordance with the specified format| +|TO_SINGLE_BYTE|Converts a multibyte string to a single-byte string| + + + - Conditional expressions + +|Item|Overview| +|:---|:---| +|DECODE|Compares values, and if they match, returns a corresponding value| +|LNNVL|Evaluates if a value is false or unknown| +|NANVL|Returns a substitute value when a value is not a number (NaN)| +|NVL|Returns a substitute value when a value is NULL| +|NVL2|Returns a substitute value based on whether a value is NULL or not NULL| + + + - Aggregate functions + +|Item|Overview| +|:---|:---| +|LISTAGG|Returns a concatenated, delimited list of string values| +|MEDIAN|Calculates the median of a set of values| + + - Functions that return internal information + +|Item|Overview| +|:---|:---| +|DUMP|Returns internal information of a value| + + + +**SQL Operators** + +|Item|Overview| +|:---|:---| +|Datetime operator|Datetime operator for the DATE type| + + +**Packages** + +|Item|Overview| +|:---|:---| +|DBMS_ALERT|Sends alerts to multiple sessions| +|DBMS_ASSERT|Validates the properties of an input value| +|DBMS_OUTPUT|Sends messages to clients| +|DBMS_PIPE|Creates a pipe for inter-session communication| +|DBMS_RANDOM|Generates random numbers| +|DBMS_UTILITY|Provides various utilities| +|UTL_FILE|Enables text file operations| + + + diff --git a/contrib/orafce/doc/orafce_documentation/Orafce_Documentation_02.md b/contrib/orafce/doc/orafce_documentation/Orafce_Documentation_02.md new file mode 100644 index 000000000..6e7a75fab --- /dev/null +++ b/contrib/orafce/doc/orafce_documentation/Orafce_Documentation_02.md @@ -0,0 +1,98 @@ +Chapter 2 Notes on Using orafce +--- + +Orafce is defined as user-defined functions in the "public" schema created by default when database clusters are created, so they can be available for all users without the need for special settings. +For this reason, ensure that "public" (without the double quotation marks) is included in the list of schema search paths specified in the search_path parameter. + +The following features provided by orafce are implemented in PostgreSQL and orafce using different external specifications. In the default configuration of PostgreSQL, the standard features of PostgreSQL take precedence. + + + +**Features implemented in PostgreSQL and orafce using different external specifications** + + - Data type + +|Item|Standard feature of PostgreSQL|Compatibility feature added by orafce| +|:---|:---|:---| +|DATE|Stores date only.|Stores date and time.| + + + - Function + +|Item|Standard feature of PostgreSQL|Compatibility feature added by orafce| +|:---|:---|:---| +|LENGTH|If the string is CHAR type, trailing spaces are not included in the length.|If the string is CHAR type, trailing spaces are included in the length.| +|SUBSTR|If 0 or a negative value is specified for the start position, simply subtracting 1 from the start position, the position will be shifted to the left, from where extraction will start.| - If 0 is specified for the start position, extraction will start from the beginning of the string.
- If a negative value is specified for the start position, extraction will start from the position counted from the end of the string.| +|LPAD
RPAD| - If the string is CHAR type, trailing spaces are removed and then the value is padded.
- The result length is handled as a number of characters.| - If the string is CHAR type, the value is padded without removing trailing spaces.
- The result length is based on the width of the displayed string. Therefore, fullwidth characters are handled using a width of 2, and halfwidth characters are handled using a width of 1.| +|LTRIM
RTRIM
BTRIM (*1)|If the string is CHAR type, trailing spaces are removed and then the value is removed.|If the string is CHAR type, the value is removed without removing trailing spaces.| +|TO_DATE|The data type of the return value is DATE.|The data type of the return value is TIMESTAMP.| + + +*1: BTRIM does not exist for Oracle databases, however, an external specification different to PostgreSQL is implemented in orafce to align with the behavior of the TRIM functions. + +Also, the following features cannot be used in the default configuration of PostgreSQL. + + + + +**Features that cannot be used in the default configuration of PostgreSQL** + + + - Function + +|Feature| +|:---| +|SYSDATE| +|DBTIMEZONE| +|SESSIONTIMEZONE| +|TO_CHAR (date/time value)| + + - Operator + +|Feature| +|:---| +|Datetime operator| + +To use these features, set "oracle" and "pg_catalog" in the "search_path" parameter of postgresql.conf. You must specify "oracle" before "pg_catalog" when doing this. + +~~~ +search_path = '"$user", public, oracle, pg_catalog' +~~~ + +**Information** + +---- + + - The search_path parameter specifies the order in which schemas are searched. Each feature compatible with Oracle databases is defined in the oracle schema. + - It is recommended to set search_path in postgresql.conf. In this case, it will be effective for each instance. + - The configuration of search_path can be done at the user level or at the database level. Setting examples are shown below. + - If the standard features of PostgreSQL take precedence, and features that cannot be used with the default configuration of PostgreSQL are not required, it is not necessary to change the settings of search_path. + + - Example of setting at the user level + - This can be set by executing an SQL command. In this example, user1 is used as the username. + +~~~ +ALTER USER user1 SET search_path = "$user",public,oracle,pg_catalog; +~~~ + + - Example of setting at the database level + - This can be set by executing an SQL command. In this example, db1 is used as the database name.
You must specify "oracle" before "pg_catalog". + +~~~ +ALTER DATABASE db1 SET search_path = "$user",public,oracle,pg_catalog; +~~~ + + + +---- + + +**See** + +---- + + - Refer to "Server Administration" > "Client Connection Defaults" > "Statement Behavior" in the PostgreSQL Documentation for information on search_path. + - Refer to "Reference" > "SQL Commands" in the PostgreSQL Documentation for information on ALTER USER and ALTER DATABASE. + +---- + diff --git a/contrib/orafce/doc/orafce_documentation/Orafce_Documentation_03.md b/contrib/orafce/doc/orafce_documentation/Orafce_Documentation_03.md new file mode 100644 index 000000000..5dea56d8b --- /dev/null +++ b/contrib/orafce/doc/orafce_documentation/Orafce_Documentation_03.md @@ -0,0 +1,118 @@ + +Chapter 3 Data Types +--- + +The following data types are supported: + + - VARCHAR2 + - NVARCHAR2 + - DATE + +### 3.1 VARCHAR2 + +**Syntax** + +![VARCHAR2](gif/VARCHAR2.gif) + +Specify the VARCHAR2 type as follows. + +|Data type syntax|Explanation| +|:---|:---| +|VARCHAR2(*len*)|String with a variable length up to *len* characters
For *len*, specify an integer greater than 0.
If *len* is omitted, the string can be any length.| + + +**General rules** + + - VARCHAR2 is a character data type. Specify the number of bytes for the length. + - Strings are of variable length. The specified value will be stored as is. The upper limit for this data type is approximately one gigabyte. + +**Note** + +---- + +The VARCHAR2 type does not support collating sequences. Therefore, the following error occurs when a collating sequence like that of an ORDER BY clause is required. +At this time, the following HINT will prompt to use a COLLATE clause, however, because collating sequences are not supported, it is not possible to use this clause. + +~~~ +ERROR: could not determine which collation to use for string comparison +HINT: Use the COLLATE clause to set the collation explicitly. +~~~ + +If the error shown above is displayed, explicitly cast the column to VARCHAR or TEXT type. + +---- + + +### 3.2 NVARCHAR2 + +**Syntax** + +![NVARCHAR2](gif/NVARCHAR2.gif) + + + + +Specify the NVARCHAR2 type as follows. + +|Data type syntax|Explanation| +|:---|:---| +|NVARCHAR2(*len*)| National character string with a variable length up to *len* characters.
For *len*, specify an integer greater than 0.
If *len* is omitted, the string can be any length.| + +**General rules** + + - NVARCHAR2 is a national character data type. Specify the number of characters for the length. + - Strings are of variable length. The specified value will be stored as is. The upper limit for this data type is approximately one gigabyte. + +**Note** + +---- + +The NVARCHAR2 type does not support collating sequences. Therefore, the following error occurs when a collating sequence like that of an ORDER BY clause is required. At this time, the following HINT will prompt to use a COLLATE clause, however, because collating sequences are not supported, it is not possible to use this clause. + +~~~ +ERROR: could not determine which collation to use for string comparison +HINT: Use the COLLATE clause to set the collation explicitly. +~~~ + +If the error shown above is displayed, explicitly cast the column to NCHAR VARYING or TEXT type. + +---- + +### 3.3 DATE + +**Syntax** + +![DATE](gif/DATE.gif) + + + + +Specify the DATE type as follows. + +|Data type syntax|Explanation| +|:---|:---| +|DATE|Stores date and time| + +**General rules** + + - DATE is a date/time data type. + - Date and time are stored in DATE. The time zone is not stored. + +**Note** + +---- + +If the DATE type of orafce is used in DDL statements such as table definitions, always set search_path before executing a DDL statement. Even if search_path is changed after definition, the data type will be the DATE type of PostgreSQL. + +---- + + +**Information** + +---- + +The DATE type of orafce is equivalent to the TIMESTAMP type of PostgreSQL. Therefore, of the existing functions of PostgreSQL, functions for which the data type of the argument is TIMESTAMP can be used. + +---- + + diff --git a/contrib/orafce/doc/orafce_documentation/Orafce_Documentation_04.md b/contrib/orafce/doc/orafce_documentation/Orafce_Documentation_04.md new file mode 100644 index 000000000..d361a6951 --- /dev/null +++ b/contrib/orafce/doc/orafce_documentation/Orafce_Documentation_04.md @@ -0,0 +1,27 @@ +Chapter 4 Queries +--- + +The following queries are supported: + + - DUAL Table + +### 4.1 DUAL Table +DUAL table is a virtual table provided by the system. +Use when executing SQL where access to a base table is not required, +such as when performing tests to get result expressions such as functions and operators. + +**Example** + +---- + +In the following example, the current system date is returned. + +~~~ +SELECT CURRENT_DATE "date" FROM DUAL; + date +------------ + 2013-05-14 +(1 row) +~~~ + +---- diff --git a/contrib/orafce/doc/orafce_documentation/Orafce_Documentation_05.md b/contrib/orafce/doc/orafce_documentation/Orafce_Documentation_05.md new file mode 100644 index 000000000..ecefea06f --- /dev/null +++ b/contrib/orafce/doc/orafce_documentation/Orafce_Documentation_05.md @@ -0,0 +1,2258 @@ +Chapter 5 SQL Function Reference +--- + +### 5.1 Mathematical Functions + +The following mathematical functions are supported: + + - BITAND + - COSH + - SINH + - TANH + +#### 5.1.1 BITAND + +**Description** + +Performs a bitwise AND operation. + +**Syntax** + +![BITAND]( gif/BITAND.gif) + + +**General rules** + + - BITAND performs an AND operation on each bit of two integers, and returns the result. + - Specify integer type values. + - The data type of the return value is BIGINT. + + +**Example** + +---- + +In the following example, the result of the AND operation on numeric literals 5 and 3 is returned. + +~~~ +SELECT BITAND(5,3) FROM DUAL; + bitand +------- + 1 +(1 row) +~~~ + +---- + + +#### 5.1.2 COSH + +**Description** + +Calculates the hyperbolic cosine of a number. + +**Syntax** + +![COSH]( gif/COSH.gif) + + +**General rules** + + - COSH returns the hyperbolic cosine of the specified number. + - The number must be a numeric data type. + - The data type of the return value is DOUBLE PRECISION. + +**Example** + +---- + +In the following example, the hyperbolic cosine of the numeric literal 2.236 is returned. + +~~~ +SELECT COSH(2.236) FROM DUAL; + cosh +----------------- +4.7313591000247 +(1 row) +~~~ + +---- + + +#### 5.1.3 SINH + +**Description** + +Calculates the hyperbolic sine of a number. + +**Syntax** + +![SINH]( gif/SINH.gif) + + +**General rules** + + - SINH returns the hyperbolic sine of the specified number. + - The number must be a numeric data type. + - The data type of the return value is DOUBLE PRECISION. + +**Example** + +---- + +In the following example, the hyperbolic sine of the numeric literal 1.414 is returned. + +~~~ +SELECT SINH(1.414) FROM DUAL; + sinh +----------------- +1.93460168824956 +(1 row) +~~~ + +---- + + +#### 5.1.4 TANH + +**Description** + +Calculates the hyperbolic tangent of a number. + +**Syntax** + +![TANH]( gif/TANH.gif) + +**General rules** + + - TANH returns the hyperbolic tangent of the specified number. + - The number must be a numeric data type. + - The data type of the return value is DOUBLE PRECISION. + +**Example** + +---- + +In the following example, the hyperbolic tangent of the numeric literal 3 is returned. + +~~~ +SELECT TANH(3) FROM DUAL; + tanh +----------------- +0.995054753686731 +(1 row) +~~~ + +---- + +### 5.2 String Functions + +The following string functions are supported: + + - BTRIM + - INSTR + - LENGTH + - LENGTHB + - LPAD + - LTRIM + - NLSSORT + - REGEXP_COUNT + - REGEXP_INSTR + - REGEXP_LIKE + - REGEXP_SUBSTR + - RPAD + - RTRIM + - SUBSTR + - SUBSTRB + +#### 5.2.1 BTRIM + +**Description** + +Removes the specified characters from the beginning and end of a string. + +**Syntax** + +![BTRIM]( gif/BTRIM.gif) + +**General rules** + + - BTRIM returns a string with *trimChars* removed from the beginning and end of string *str*. + - If multiple trim characters are specified, all characters matching the trim characters are removed. If *trimChars* is omitted, all leading and trailing halfwidth spaces are removed. + - The data type of the return value is TEXT. + +**Note** + +---- + + - BTRIM does not exist for Oracle databases. + - The CHAR type specification for BTRIM uses orafce for its behavior, which is different to that of BTRIM of PostgreSQL. The search_path parameter must be modified for it to behave the same as the specification described above. + +---- + + +**Information** + +---- + +The general rule for BTRIM of PostgreSQL is as follows: + + - If the string is CHAR type, trailing spaces are removed and then the trim characters are removed. + +---- + + +**See** + +---- + + - Refer to "Notes on Using orafce" for information on how to edit search_path. + - Refer to "The SQL Language" > "Functions and Operators" > "String Functions and Operators" in the PostgreSQL Documentation for information on BTRIM. + +---- + + +**Example** + +---- + +In the following example, a string that has had "a" removed from both ends of "aabcaba" is returned. + +~~~ +SELECT BTRIM('aabcaba','a') FROM DUAL; + btrim +------- + bcab +(1 row) +~~~ + +---- + +#### 5.2.2 INSTR + +**Description** + +Returns the position of a substring in a string. + +**Syntax** + +![INSTR]( gif/INSTR.gif) + + +**General rules** + + - INSTR searches for substring *str2* in string *str1* and returns the position (in characters) in *str1* of the first character of the occurrence. + - The search starts from the specified start position *startPos* in *str1*. + - When *startPos* is 0 or negative, the start position will be the specified number of characters from the left of the end of *str1*, and INSTR will search backward from that point. + - If the start position is not specified, the search will be performed from the beginning of *str1*. + - If *occurrences* is specified, the position in *str1* of the nth occurrence of *str2* is returned. Only positive numbers can be specified. + - If *occurrences* is not specified, the start position of the first occurrence that is found is returned. + - If *str2* is not found in *str1*, 0 is returned. + - For *startPos* and *occurrences*, specify a SMALLINT or INTEGER type. + - The data type of the return value is INTEGER. + +**Example** + +---- + +In the following example, characters "BC" are found in string "ABCACBCAAC", and the position of those characters is returned. + +~~~ +SELECT INSTR('ABCACBCAAC','BC') FROM DUAL; + instr +------- + 2 +(1 row) + +SELECT INSTR('ABCACBCAAC','BC',-1,2) FROM DUAL; + instr +------- + 2 +(1 row) +~~~ + +---- + +#### 5.2.3 LENGTH + +**Description** + +Returns the length of a string in number of characters. + + +**Syntax** + +![LENGTH]( gif/LENGTH.gif) + + +**General rules** + + - LENGTH returns the number of characters in string *str*. + - If the string is CHAR type, trailing spaces are included in the length. + - The data type of the return value is INTEGER. + +**Note** + +---- + +The LENGTH specification above uses orafce for its behavior, which is different to that of LENGTH of PostgreSQL. The search_path parameter must be modified for it to behave according to the orafce specification. + +---- + + +**Information** + +---- + +The general rule for LENGTH of PostgreSQL is as follows: + + - If the string is CHAR type, trailing spaces are not included in the length. + +---- + + +**See** + +---- + + - Refer to "Notes on Using orafce" for information on how to edit search_path. + - Refer to "The SQL Language" > "Functions and Operators" > "String Functions and Operators" in the PostgreSQL Documentation for information on LENGTH. + +---- + + + +**Example** + +---- + +In the following example, the number of characters in column col2 (defined using CHAR(10)) in table t1 is returned. + +~~~ +SELECT col2,LENGTH(col2) FROM t1 WHERE col1 = '1001'; + col2 | length +------------+-------- + AAAAA | 10 +(1 row) +~~~ + +---- + +#### 5.2.4 LENGTHB + +**Description** + +Returns the length of a string in number of bytes. + +**Syntax** + +![LENGTHB]( gif/LENGTHB.gif) + + +**General rules** + + - LENGTHB returns the number of bytes in string *str*. + - If the string is CHAR type, trailing spaces are included in the length. + - The data type of the return value is INTEGER. + +**Example** + +---- + +In the following example, the number of bytes in column col2 (defined using CHAR(10)) in table t1 is returned. +Note that, in the second SELECT statement, each character in "\*" has a length of 3 bytes, for a total of 9 bytes, and 7 bytes are added for the 7 trailing spaces. This gives a result of 16 bytes. + +~~~ +SELECT col2,LENGTHB(col2) FROM t1 WHERE col1 = '1001'; + col2 | lengthb +---------------+--------- + AAAAA | 10 +(1 row) + +SELECT col2,LENGTHB(col2) FROM t1 WHERE col1 = '1004'; + col2 | lengthb +---------------+--------- + *** | 16 +(1 row) +~~~ + +---- + + +#### 5.2.5 LPAD + +**Description** + +Left-pads a string to a specified length with a sequence of characters. + +**Syntax** + +![LPAD]( gif/LPAD.gif) + +**General rules** + + - LPAD returns the result after repeatedly padding the beginning of string *str* with padding characters *paddingStr* until the string reaches length *len*. + - If the string is CHAR type, the padding characters are added to the string without removing trailing spaces. + - In the resultant string, fullwidth characters are recognized as having a length of 2, and halfwidth characters having a length of 1. If a fullwidth character cannot be included in the resultant string because there is only space available for one halfwidth character, the string is padded with a single-byte space. + - The data type of the return value is TEXT. + +**Note** + +---- + +The LPAD specification above uses orafce for its behavior, which is different to that of LPAD of PostgreSQL. The search_path parameter must be modified for it to behave according to the orafce specification. + +---- + + +**Information** + +---- + +The general rules for LPAD of PostgreSQL are as follows: + + - If the string is CHAR type, trailing spaces are removed and then the padding characters are added to the string. + - The result length is the number of characters. + +---- + + +**See** + +---- + + - Refer to "Notes on Using orafce" for information on how to edit search_path. + - Refer to "The SQL Language" > "Functions and Operators" > "String Functions and Operators" in the PostgreSQL Documentation for information on LPAD. + +---- + +**Example** + +---- + +In the following example, a 10-character string that has been formed by left-padding the string "abc" with "a" is returned. + +~~~ +SELECT LPAD('abc',10,'a') FROM DUAL; + lpad +------------ + aaaaaaaabc +(1 row) +~~~ + +---- + + +#### 5.2.6 LTRIM + +**Description** + +Removes the specified characters from the beginning of a string. + +**Syntax** + +![LTRIM]( gif/LTRIM.gif) + +**General rules** + + - LTRIM returns a string with *trimChars* removed from the beginning of string *str*. + - If multiple trim characters are specified, all characters matching the trim characters are removed. If *trimChars* is omitted, all leading halfwidth spaces are removed. + - The data type of the return value is TEXT. + +**Note** + +---- + +The LTRIM specification above uses orafce for its behavior, which is different to that of LTRIM of PostgreSQL. The search_path parameter must be modified for it to behave according to the orafce specification. + +---- + + +**Information** + +---- + +The general rule for LTRIM of PostgreSQL is as follows: + + - If the string is CHAR type, trailing spaces are removed and then the trim characters are removed. + +---- + +**See** + +---- + + - Refer to "Notes on Using orafce" for information on how to edit search_path. + - Refer to "The SQL Language" > "Functions and Operators" > "String Functions and Operators" in the PostgreSQL Documentation for information on LTRIM. + +---- + + + +**Example** + +---- + +In the following example, a string that has had "ab" removed from the beginning of "aabcab" is returned. + +~~~ +SELECT LTRIM('aabcab','ab') FROM DUAL; + ltrim +------- + cab +(1 row) +~~~ + +---- + + +#### 5.2.7 NLSSORT + +**Description** + +Returns a byte string that denotes the lexical order of the locale (COLLATE). + +**Syntax** + +![NLSSORT]( gif/NLSSORT.gif) + +**General rules** + + - NLSSORT is used for comparing and sorting in the collating sequence of a locale (COLLATE) that differs from the default locale. + - Values that can be specified for the locale differ according to the operating system of the database server. + - If the locale is omitted, it is necessary to use set_nls_sort to set the locale in advance. To set the locale using set_nls_sort, execute a SELECT statement. + +**Example of setting set_nls_sort using a SELECT statement** + +~~~ +SELECT set_nls_sort('en_US.UTF8'); +~~~ + + - The data type of the return value is BYTEA. + +**Note** + +---- + +If specifying locale encoding, ensure it matches the database encoding. + +---- + + +**See** + +---- + +Refer to "Server Administration" > "Localization" > "Locale Support" in the PostgreSQL Documentation for information on the locales that can be specified. + +---- + +**Example** + +---- + +[Composition of table (t3)] + +|col1 | col2| +|:--- |:--- | +|1001 |aabcababc| +|2001 |abcdef| +|3001 |aacbaab| + +In the following example, the result of sorting column col2 in table t3 by "da_DK.UTF8" is returned. + +~~~ +SELECT col1,col2 FROM t3 ORDER BY NLSSORT(col2,'da_DK.UTF8'); + col1 | col2 +------+------------ + 2001 | abcdef + 1001 | aabcababc + 3001 | aacbaab +(3 row) +~~~ + +---- + +#### 5.2.8 REGEXP_COUNT + +**Description** + +Searches a string for a regular expression, and returns a count of the matches. + +**Syntax** + +![REGEXP_COUNT]( gif/REGEXP_COUNT.gif) + +**General rules** + + - REGEXP_COUNT returns the number of times *pattern* occurs in a source *string*. It returns an integer indicating the number of occurrences of *pattern*. If no match is found, then the function returns 0. + - The search starts from the specified start position *startPos* in *string*, default starts from the beginning of *string*. + - *startPos* is a positive integer, negative values to search from the end of *string* are not allowed. + - *flags* is a character expression that lets you change the default matching behavior of the function. + The value of *flags* can include one or more of the following characters: + - 'i': case-insensitive matching. + - 'c': case-sensitive and accent-sensitive matching. + - 'n': the period (.) match the newline character. By default the period does not match the newline character. + - 'm': treats the source string as multiple lines. + - 'x': ignores whitespace characters. By default, whitespace characters match themselves. + If you omit *flags*, then: + - The default is case and accent sensitivity. + - A period (.) does not match the newline character. + - The source string is treated as a single line. + +**Example** + +~~~ +SELECT REGEXP_COUNT('a'||CHR(10)||'d', 'a.d') FROM DUAL; + regexp_count +-------------- + 0 +(1 row) + +SELECT REGEXP_COUNT('a'||CHR(10)||'d', 'a.d', 1, 'm') FROM DUAL; + regexp_count +-------------- + 0 +(1 row) + +SELECT REGEXP_COUNT('a'||CHR(10)||'d', 'a.d', 1, 'n') FROM DUAL; + regexp_count +-------------- + 1 +(1 row) + +SELECT REGEXP_COUNT('a'||CHR(10)||'d', '^d$', 1, 'm') FROM DUAL; + regexp_count +-------------- + 1 +(1 row) +~~~ + +---- + +#### 5.2.9 REGEXP_INSTR + +**Description** + +Returns the beginning or ending position within the string where the match for a pattern was located. + +**Syntax** + +![REGEXP_INSTR]( gif/REGEXP_INSTR.gif) + +**General rules** + + - REGEXP_INSTR returns an integer indicating the beginning or ending position of the matched substring, depending on the value of the *return_opt* argument. If no match is found, then the function returns 0. + - The search starts from the specified start position *startPos* in *string*, default starts from the beginning of *string*. + - *startPos* is a positive integer, negative values to search from the end of *string* are not allowed. + - *occurrence* is a positive integer indicating which occurrence of *pattern* in *string* should be search for. The default is 1, meaning the first occurrence of *pattern* in *string*. + - *return_opt* lets you specify what should be returned in relation to the occurrence: + - 0, the position of the first character of the occurrence is returned. This is the default. + - 1, the position of the character following the occurrence is returned. + - *flags* is a character expression that lets you change the default matching behavior of the function. See [REGEXP_COUNT](#REGEXP_COUNT) for detailed information. + - For a *pattern* with capture group, *group* is a positive integer indicating which capture group in *pattern* shall be returned by the function. Capture groups can be nested, they are numbered in order in which their left parentheses appear in *pattern*. If *group* is zero, then the position of the entire substring that matches the pattern is returned. If *group* value exceed the number of capture groups in *pattern*, the function returns zero. A null *group* value returns *NULL*. The default value for *group* is zero. + +**Example** + +~~~ +SELECT REGEXP_INSTR('1234567890', '(123)(4(56)(78))') FROM DUAL; + regexp_instr +-------------- + 1 +(1 row) + +SELECT REGEXP_INSTR('1234567890', '(4(56)(78))', 3) FROM DUAL; + regexp_instr +-------------- + 4 +(1 row) + +SELECT REGEXP_INSTR('123 123456 1234567, 1234567 1234567 12', '[^ ]+', 1, 6) FROM DUAL; + regexp_instr +-------------- + 37 + +(1 row) + +SELECT REGEXP_INSTR('199 Oretax Prayers, Riffles Stream, CA', '[S|R|P][[:alpha:]]{6}', 3, 2, 1) FROM DUAL; + regexp_instr +-------------- + 28 +(1 row) +~~~ + +---- + +#### 5.2.10 REGEXP_LIKE + +**Description** + +Condition in the WHERE clause of a query, causing the query to return rows that match the given pattern. + +**Syntax** + +![REGEXP_LIKE]( gif/REGEXP_LIKE.gif) + +**General rules** + + - REGEXP_LIKE is similar to the LIKE condition, except it performs regular expression matching instead of the simple pattern matching performed by LIKE. + - Returns a boolean, *true* when *pattern* match in *string*, *false* otherwise. + - *flags* is a character expression that lets you change the default matching behavior of the function. See [REGEXP_COUNT](#REGEXP_COUNT) for detailed information. + +**Example** + +~~~ +SELECT REGEXP_LIKE('a'||CHR(10)||'d', 'a.d', 'm') FROM DUAL; + regexp_like +------------- + f +(1 row) + +SELECT REGEXP_LIKE('a'||CHR(10)||'d', 'a.d', 'n') FROM DUAL; + regexp_like +------------- + t +(1 row) +~~~ + +---- + +#### 5.2.11 REGEXP_SUBSTR + +**Description** + +Returns the string that matches the pattern specified in the call to the function. + +**Syntax** + +![REGEXP_SUBSTR]( gif/REGEXP_SUBSTR.gif) + +**General rules** + + - REGEXP_SUBSTR returns the matched substring resulting from matching a POSIX regular expression pattern to a string. If no match is found, then the function returns *NULL*. + - The search starts from the specified start position *startPos* in *string*, default starts from the beginning of *string*. + - *startPos* is a positive integer, negative values to search from the end of *string* are not allowed. + - *occurrence* is a positive integer indicating which occurrence of *pattern* in *string* should be search for. The default is 1, meaning the first occurrence of *pattern* in *string*. + - *flags* is a character expression that lets you change the default matching behavior of the function. See [REGEXP_COUNT](#REGEXP_COUNT) for detailed information. + - For a *pattern* with capture group, *group* is a positive integer indicating which capture group in *pattern* shall be returned by the function. Capture groups can be nested, they are numbered in order in which their left parentheses appear in *pattern*. If *group* is zero, then the position of the entire substring that matches the pattern is returned. If *group* value exceed the number of capture groups in *pattern*, the function returns *NULL*. A null *group* value returns *NULL*. The default value for *group* is zero. + +**Example** + +~~~ +SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',[^,]+') FROM DUAL; + regexp_substr +---------------- + , zipcode town +(1 row) + +SELECT regexp_substr('number of your street, zipcode town, FR', ',[^,]+', 24) FROM DUAL; + regexp_substr +--------------- + , FR +(1 row) + +SELECT regexp_substr('number of your street, zipcode town, FR', ',[^,]+', 1, 2) FROM DUAL; + regexp_substr +--------------- + , FR +(1 row) + +SELECT regexp_substr('1234567890 1234567890', '(123)(4(56)(78))', 1, 1, 'i', 0) FROM DUAL; + regexp_substr +--------------- + 12345678 +(1 row) +~~~ + +---- + +#### 5.2.12 REGEXP_REPLACE + +**Description** + +Returns the string that matches the pattern specified in the call to the function. + +**Syntax** + +![REGEXP_REPLACE]( gif/REGEXP_REPLACE.gif) + +**General rules** + + - REGEXP_REPLACE returns a modified version of the source string where occurrences of a POSIX regular expression pattern found in the source string are replaced with the specified replacement string. If no match is found or the occurrence queried exceed the number of match, then the source string untouched is returned. + - The search and replacement starts from the specified start position *startPos* in *string*, default starts from the beginning of *string*. + - *startPos* is a positive integer, negative values to search from the end of *string* are not allowed. + - *occurrence* is a positive integer indicating which occurrence of *pattern* in *string* should be search for and replaced. The default is 0, meaning all occurrences of *pattern* in *string*. + - *flags* is a character expression that lets you change the default matching behavior of the function. See [REGEXP_COUNT](#REGEXP_COUNT) for detailed information. + +**Example** + +~~~ +SELECT regexp_replace('512.123.4567 612.123.4567', '([[:digit:]]{3})\.([[:digit:]]{3})\.([[:digit:]]{4})', '(\1) \2-\3') FROM DUAL; + regexp_replace +------------------------------- + (512) 123-4567 (612) 123-4567 +(1 row) + +SELECT oracle.REGEXP_REPLACE('number your street, zipcode town, FR', '( ){2,}', ' ', 9); + regexp_replace +---------------------------------------- + number your street, zipcode town, FR +(1 row) + +SELECT oracle.REGEXP_REPLACE('number your street, zipcode town, FR', '( ){2,}', ' ', 9, 2); + regexp_replace +--------------------------------------------- + number your street, zipcode town, FR +(1 row) +~~~ + +---- + +#### 5.2.13 RPAD + +**Description** + +Right-pads a string to a specified length with a sequence of characters. + +**Syntax** + +![RPAD]( gif/RPAD.gif) + +**General rules** + + - RPAD returns the result after repeatedly padding the end of string *str* with padding characters *paddingStr* until the string reaches length *len*. + - If the string is CHAR type, the padding characters are added to the string without removing trailing spaces. + - In the resultant string, fullwidth characters are recognized as having a length of 2, and halfwidth characters having a length of 1. If a fullwidth character cannot be included in the resultant string because there is only space available for one halfwidth character, the string is padded with a single-byte space. + - The data type of the return value is TEXT. + +**Note** + +---- + +The RPAD specification above uses orafce for its behavior, which is different to that of RPAD of PostgreSQL. The search_path parameter must be modified for it to behave according to the orafce specification. + +---- + + +**Information** + +---- + +The general rules for RPAD of PostgreSQL are as follows: + + - If the string is CHAR type, trailing spaces are removed and then the padding characters are added to the string. + - The result length is the number of characters. + +---- + +**See** + +---- + + - Refer to "Notes on Using orafce" for information on how to edit search_path. + - Refer to "The SQL Language" > "Functions and Operators" > "String Functions and Operators" in the PostgreSQL Documentation for information on RPAD. + +---- + +**Example** + +---- + +In the following example, a 10-character string that has been formed by right-padding the string "abc" with "a" is returned. + +~~~ +SELECT RPAD('abc',10,'a') FROM DUAL; + rpad +------------ + abcaaaaaaa +(1 row) +~~~ + +---- + +#### 5.2.14 RTRIM + +**Description** + +Removes the specified characters from the end of a string. + + +**Syntax** + +![RTRIM]( gif/RTRIM.gif) + +**General rules** + + - RTRIM returns a string with *trimChars* removed from the end of string *str*. + - If multiple trim characters are specified, all characters matching the trim characters are removed. If *trimChars* is omitted, all trailing halfwidth spaces are removed. + - The data type of the return value is TEXT. + + +**Note** + +---- + +The RTRIM specification above uses orafce for its behavior, which is different to that of RTRIM of PostgreSQL. The search_path parameter must be modified for it to behave the same as the orafce specification. + +---- + +**Information** + +---- + +The general rule for RTRIM of PostgreSQL is as follows: + + - If the string is CHAR type, trailing spaces are removed and then the trim characters are removed. + +---- + + +**See** + +---- + + - Refer to "Notes on Using orafce" for information on how to edit search_path. + - Refer to "The SQL Language" > "Functions and Operators" > "String Functions and Operators" in the PostgreSQL Documentation for information on RTRIM. + +---- + +**Example** + +---- + +In the following example, a string that has had "ab" removed from the end of "aabcab" is returned. + +~~~ +SELECT RTRIM('aabcab','ab') FROM DUAL; + rtrim +------- + aabc +(1 row) +~~~ + +---- + +#### 5.2.15 SUBSTR + +**Description** + +Extracts part of a string using characters to specify position and length. + +**Syntax** + +![SUBSTR]( gif/SUBSTR.gif) + +**General rules** + + - SUBSTR extracts and returns a substring of string *str*, beginning at position *startPos*, for number of characters *len*. + - When *startPos* is positive, it will be the number of characters from the beginning of the string. + - When *startPos* is 0, it will be treated as 1. + - When *startPos* is negative, it will be the number of characters from the end of the string. + - When *len* is not specified, all characters to the end of the string are returned. NULL is returned when *len* is less than 1. + - For *startPos* and *len*, specify an integer or NUMERIC type. If numbers including decimal places are specified, they are truncated to integers. + - The data type of the return value is TEXT. + +**Note** + +---- + + - There are two types of SUBSTR. One that behaves as described above and one that behaves the same as SUBSTRING. The search_path parameter must be modified for it to behave the same as the specification described above. + - If the change has not been implemented, SUBSTR is the same as SUBSTRING. + +---- + + +**Information** + +---- + +The general rules for SUBSTRING are as follows: + + - The start position will be from the beginning of the string, whether the start position is positive, 0, or negative. + - When *len* is not specified, all characters to the end of the string are returned. + - An empty string is returned if no string is extracted or *len* is less than 1. + +---- + + + +**See** + +---- + +Refer to "The SQL Language" > "Functions and Operators" > "String Functions and Operators" in the PostgreSQL Documentation for information on SUBSTRING. + +---- + + +**Example** + +---- + +In the following example, part of the string "ABCDEFG" is extracted. + +~~~ +SELECT SUBSTR('ABCDEFG',3,4) "Substring" FROM DUAL; + + Substring +----------- + CDEF +(1 row) + +SELECT SUBSTR('ABCDEFG',-5,4) "Substring" FROM DUAL; + + Substring +----------- + CDEF +(1 row) +~~~ + +---- + + +#### 5.2.16 SUBSTRB + +**Description** + +Extracts part of a string using bytes to specify position and length. + +**Syntax** + +![SUBSTRB]( gif/SUBSTRB.gif) + +**General rules** + + - SUBSTRB extracts and returns a substring of string *str*, beginning at byte position *startPos*, for number of bytes *len*. + - When *startPos* is 0 or negative, extraction starts at the position found by subtracting 1 from the start position and shifting by that number of positions to the left. + - When *len* is not specified, all bytes to the end of the string are returned. + - An empty string is returned if no string is extracted or *len* is less than 1. + - For *startPos* and *len*, specify a SMALLINT or INTEGER type. + - The data type of the return value is VARCHAR2. + +**Note** + +---- + +The external specification of SUBSTRB is different to that of SUBSTR added by orafce, conforming with SUBSTRING of PostgreSQL. + +---- + + +**Example** + +---- + +In the following example, part of the string "aaabbbccc" is extracted. + +~~~ +SELECT SUBSTRB('aaabbbccc',4,3) FROM DUAL; + substrb +----------- + bbb +(1 row) + +SELECT SUBSTRB('aaabbbccc',-2,6) FROM DUAL; + substrb +----------- + aaa +(1 row) +~~~ + +---- + +### 5.3 Date/time Functions + +The following date/time functions are supported: + + - ADD_MONTHS + - DBTIMEZONE + - LAST_DAY + - MONTHS_BETWEEN + - NEXT_DAY + - ROUND + - SESSIONTIMEZONE + - SYSDATE + - TRUNC + +**Note** + +---- + +If the DATE type only is shown in the date/time functions, these functions can be used in both orafce and PostgreSQL. + +---- + +#### 5.3.1 ADD_MONTHS + +**Description** + +Adds months to a date. + +**Syntax** + +![ADD_MONTHS]( gif/ADD_MONTHS.gif) + +**General rules** + + - ADD_MONTHS returns *date* plus *months*. + - For *date*, specify a DATE type. + - For *months*, specify a SMALLINT or INTEGER type. + - If a negative value is specified for *months*, the number of months is subtracted from the date. + - The data type of the return value is DATE. + +**Note** + +---- + +If using the DATE type of orafce, it is necessary to specify "oracle" for search_path in advance. + +---- + +**See** + +---- + +Refer to "Notes on Using orafce" for information on how to edit search_path. + +---- + +**Example** + +---- + +The example below shows the result of adding 3 months to the date May 1, 2016. + +~~~ +SELECT ADD_MONTHS(DATE'2016/05/01',3) FROM DUAL; + add_months +--------------------- + 2016-08-01 00:00:00 +(1 row) +~~~ + +---- + +#### 5.3.2 DBTIMEZONE + +**Description** + +Returns the value of the database time zone. + +**Syntax** + +![DBTIMEZONE]( gif/DBTIMEZONE.gif) + +**General rules** + + - DBTIMEZONE returns the time zone value of the database. + - The data type of the return value is TEXT. + +**Note** + +---- + + - If using DBTIMEZONE, it is necessary to specify "oracle" for search_path in advance. + - The time zone of the database is set to "GMT" by default. To change the time zone, change the "orafce.timezone" parameter. An example using the SET statement is shown below. + +Setting example of orafce.timezone using a SET statement + +~~~ +SET orafce.timezone = 'Japan'; +~~~ + + - The orafce.timezone settings can be set using any of the methods for setting server parameters. + - If the SQL statement is executed with orafce.timezone set, the following message may be displayed, however, the parameter settings are enabled, so you can ignore this. + +~~~ +WARNING: unrecognized configuration parameter "orafce.timezone" +~~~ + + - The time zones that can be set in "orafce.timezone" are the same as for the "TimeZone" server parameter. + +---- + + +**See** + +---- + + - Refer to "Notes on Using orafce" for information on how to edit search_path. + - Refer to "The SQL Language" > "Data Types" > "Date/Time Types" in the PostgreSQL Documentation for information on the time zone. + +---- + +**Example** + +---- + +In the following example, the DBTIMEZONE result is returned. + +~~~ +SELECT DBTIMEZONE() FROM DUAL; + dbtimezone +------------ + GMT +(1 row) +~~~ + +---- + +#### 5.3.3 LAST_DAY + +**Description** + +Returns the last day of the month in which the specified date falls. + +**Syntax** + +![LAST_DAY]( gif/LAST_DAY.gif) + +**General rules** + + - LAST_DAY returns the last day of the month in which the specified date falls. + - For *date*, specify a DATE type. + - The data type of the return value is DATE. + +**Note** + +---- + +If using the DATE type of orafce, it is necessary to specify "oracle" for search_path in advance. + +---- + +**See** + +---- + +Refer to "Notes on Using orafce" for information on how to edit search_path. + +---- + +**Example** + +---- + +In the example below, the last date of "February 01, 2016" is returned. + +~~~ +SELECT LAST_DAY(DATE'2016/02/01') FROM DUAL; + last_day +--------------------- + 2016-02-29 00:00:00 +(1 row) +~~~ + +---- + + +#### 5.3.4 MONTHS_BETWEEN + +**Description** + +Returns the number of months between two dates. + +**Syntax** + +![MONTHS_BETWEEN]( gif/MONTHS_BETWEEN.gif) + +**General rules** + + - MONTHS_BETWEEN returns the difference in the number of months between *date1* and *date2*. + - For *date1* and *date2*, specify a DATE type. + - If *date2* is earlier than *date1*, the return value will be negative. + - If two dates fall on the same day, or each of the two dates are the last day of the month to which they belong, an integer is returned. If the days are different, one month is considered to be 31 days, and a value with the difference in the number of days divided by 31 added is returned. + - The data type of the return value is NUMERIC. + +**Note** + +---- + +If using the DATE type of orafce, it is necessary to specify "oracle" for search_path in advance. + +---- + +**See** + +---- + +Refer to "Notes on Using orafce" for information on how to edit search_path. + +---- + +**Example** + +---- + +In the following example, the difference between the months of March 15, 2016 and November 15, 2015 is returned. + +~~~ +SELECT MONTHS_BETWEEN(DATE'2016/03/15', DATE'2015/11/15') FROM DUAL; + months_between +---------------- + 4 +(1 row) +~~~ + +---- + +#### 5.3.5 NEXT_DAY + +**Description** + +Returns the date of the first instance of a particular day of the week that follows the specified date. + +**Syntax** + +![NEXT_DAY]( gif/NEXT_DAY.gif) + +**General rules** + + - NEXT_DAY returns the date matching the first instance of *dayOfWk* that follows *date*. + - For *date*, specify a DATE type. + - Specify a numeric value or string indicating the day of the week. + +**Values that can be specified for the day** + +|Setting example|Overview| +|:---|:---| +|1|1 (Sunday) to 7 (Saturday) can be specified| +|'Sun', or 'Sunday'|English display of the day| +|'*'|Japanese display of the day| + + - The data type of the return value is DATE. + +**Note** + +---- + + - If using the DATE type of orafce, it is necessary to specify "oracle" for search_path in advance. + - The ability to use Japanese for entering days is provided by the orafce proprietary specification. Japanese cannot be used for entering days when using date/time functions other than NEXT_DAY (such as TO_DATE). + +---- + +**See** + +---- + +Refer to "Notes on Using orafce" for information on how to edit search_path. + +---- + +**Example** + +---- + +In the example below, the date of the first Friday on or after "May 1, 2016" is returned. + +~~~ +SELECT NEXT_DAY(DATE'2016/05/01', 'Friday') FROM DUAL; + next_day +--------------------- + 2016-05-06 00:00:00 +(1 row) +~~~ + +---- + +#### 5.3.6 ROUND + +**Description** + +Rounds a date. + +**Syntax** + +![ROUND]( gif/ROUND.gif) + +**General rules** + + - ROUND returns a date rounded to the unit specified by format model *fmt*. + - For *date*, specify a DATE or TIMESTAMP type. + - Specify the format model as a string. + +**Values that can be specified for the format model** + +|Format model|Rounding unit| +|:---|:---| +|Y,YY,YYY,YYYY,
SYYYY,YEAR,SYEAR|Year| +|I,IY,IYY,IYYY|Year (values including calendar weeks, in compliance with the ISO standard)| +|Q|Quarter| +|WW|Week (first day of the year)| +|IW|Week (Monday of that week)| +|W|Week (first weekday on which the first day of the month falls)| +|DAY,DY,D|Week (Sunday of that week)| +|MONTH,MON,MM,RM|Month| +|CC,SCC|Century| +|DDD,DD,J|Day| +|HH,HH12,HH24|Hour| +|MI|Minute| + + + - If decimal places are rounded: for year, the boundary for rounding is July 1; for month, the day is 16; and for week, the weekday is Thursday. + - If *fmt* is omitted, the date is rounded by day. + - If the DATE type of PostgreSQL is specified for the date, that DATE type will be the data type of the return value. If the TIMESTAMP type is specified for the date, the data type will be TIMESTAMP WITH TIME ZONE, irrespective of whether a time zone is used. + +**Example** + +---- + +In the example below, the result of "June 20, 2016 18:00:00" rounded by Sunday of the week is returned. + +~~~ +SELECT ROUND(TIMESTAMP'2016/06/20 18:00:00','DAY') FROM DUAL; + round +------------------------ + 2016-06-19 00:00:00+09 +(1 row) +~~~ + +---- + + +#### 5.3.7 SESSIONTIMEZONE + +**Description** + +Returns the time zone of the session. + +**Syntax** + +![SESSIONTIMEZONE]( gif/SESSIONTIMEZONE.gif) + +**General rules** + + - SESSIONTIMEZONE returns the time zone value between sessions. + - The data type of the return value is TEXT. + +**Note** + +---- + + - If using SESSIONTIMEZONE, it is necessary to specify "oracle" for search_path in advance. + - The value returned by SESSIONTIMEZONE becomes the value set in the "TimeZone" server parameter. + +---- + +**See** + +---- + +Refer to "Notes on Using orafce" for information on how to edit search_path. + +---- + + +**Example** + +---- + +In the following example, the time zone of the session is returned. + +~~~ +SELECT SESSIONTIMEZONE() FROM DUAL; + sessiontimezone +----------------- + Japan +(1 row) +~~~ + +---- + +#### 5.3.8 SYSDATE + +**Description** + +Returns the system date. + +**Syntax** + +![SYSDATE]( gif/SYSDATE.gif) + +**General rules** + + - SYSDATE returns the system date. + - The data type of the return value is the DATE type of orafce. + +**Note** + +---- + + - If using SYSDATE, it is necessary to specify "oracle" for search_path in advance. + - The date returned by SYSDATE depends on the time zone value of the orafce database. + +---- + + +**See** + +---- + + - Refer to "Notes on Using orafce" for information on how to edit search_path. + - Refer to "DBTIMEZONE" for information on the time zone values of the database. + - Refer to "The SQL Language" > "Data Types" > "Date/Time Types" in the PostgreSQL Documentation for information on the time zone. + +---- + +**Example** + +---- + +In the following example, the system date is returned. + +~~~ +SELECT SYSDATE() FROM DUAL; + sysdate +--------------------- + 2016-06-22 08:06:51 +(1 row) +~~~ + +---- + + +#### 5.3.9 TRUNC + +**Description** + +Truncates a date. + +**Syntax** + +![TRUNC]( gif/TRUNC.gif) + +**General rules** + + - TRUNC returns a date truncated to the unit specified by format model *fmt*. + - For *date*, specify a DATE or TIMESTAMP type. + - Specify the format model as a string. The values that can be specified are the same as for ROUND. + - If *fmt* is omitted, the date is truncated by day. + - If the DATE type of PostgreSQL is specified for the date, that DATE type will be the data type of the return value. If the TIMESTAMP type is specified for the date, the data type will be TIMESTAMP WITH TIME ZONE, irrespective of whether a time zone is used. + +**See** + +---- + +Refer to "ROUND" for information on the values that can be specified for the format model. + +---- + +**Example** + +---- + +In the example below, the result of "August 10, 2016 15:30:00" truncated by the day is returned. + +~~~ +SELECT TRUNC(TIMESTAMP'2016/08/10 15:30:00','DDD') FROM DUAL; + trunc +------------------------ + 2016-08-10 00:00:00+09 +(1 row) +~~~ + +---- + +### 5.4 Data Type Formatting Functions + +The following data type formatting functions are supported: + +- TO_CHAR +- TO_DATE +- TO_MULTI_BYTE +- TO_NUMBER +- TO_SINGLE_BYTE + +#### 5.4.1 TO_CHAR + +**Description** + +Converts a value to a string. + +**Syntax** + +![TO_CHAR]( gif/TO_CHAR.gif) + +**General rules** + + - TO_CHAR converts the specified number or date/time value to a string. + - For *num*, specify a numeric data type. + - For *date*, specify a DATE or TIMESTAMP type. Also, you must set a date/time format for the orafce.nls_date_format variable in advance. A setting example using the SET statement is shown below. +Setting example of orafce.nls_date_format using a SET statement + +~~~ +SET orafce.nls_date_format = 'YYYY/MM/DD HH24:MI:SS'; +~~~ + + - The data type of the return value is TEXT. + +**Note** + +---- + + - If using TO_CHAR for specifying date/time values, it is necessary to specify "oracle" for search_path in advance. + - The orafce.nls_date_format settings can be set using any of the methods for setting server parameters. + - If orafce.nls_date_format is set, the following message may be displayed when an SQL statement is executed, however, the parameter settings are enabled, so you can ignore this. + +~~~ +WARNING: unrecognized configuration parameter "orafce.nls_date_format" +~~~ + +---- + +**See** + +---- + + - Refer to "Notes on Using orafce" for information on how to edit search_path. + - Refer to "Server Administration" > "Server Configuration" > "Setting Parameters" in the PostgreSQL Documentation for information on how to set the server parameters. + +---- + +**Example** + +---- + +In the following example, the numeric value "123.45" is returned as a string. + +~~~ +SELECT TO_CHAR(123.45) FROM DUAL; + to_char +--------- + 123.45 +(1 row) +~~~ + +---- + +#### 5.4.2 TO_DATE + +**Description** + +Converts a string to a date in accordance with the specified format. + +**Syntax** + +![TO_DATE]( gif/TO_DATE.gif) + +**General rules** + + - TO_DATE converts string *str* to a date in accordance with the specified format *fmt*. + - Specify a string indicating the date/time. + - Specify the required date/time format. If omitted, the format specified in the oracle.nls_date_format variable is used. If the oracle.nls_date_format variable has not been set, the existing date/time input interpretation is used. A setting example using the SET statement is shown below. + +**Setting example of orafce.nls_date_format using a SET statement** + +~~~ +SET orafce.nls_date_format = 'YYYY/MM/DD HH24:MI:SS'; +~~~ + + - The data type of the return value is TIMESTAMP. + +**Note** + +---- + + - The above TO_DATE specification uses orafce for its behavior, which is different to that of TO_DATE of PostgreSQL. The search_path parameter must be modified for it to behave according to the orafce specification. + - The orafce.nls_date_format settings can be set using any of the methods for setting server parameters. + - If orafce.nls_date_format is set, the following message may be displayed when an SQL statement is executed, however, the parameter settings are enabled, so you can ignore this. + +~~~ +WARNING: unrecognized configuration parameter "orafce.nls_date_format" +~~~ + +---- + +**Information** + +---- + +The general rule for TO_DATE for specifying the data type format of PostgreSQL is as follows: + + - The data type of the return value is the DATE type of PostgreSQL. + +---- + + +**See** + +---- + + - Refer to "Notes on Using orafce" for information on how to edit search_path. + - Refer to "The SQL Language" > "Functions and Operators" > "Data Type Formatting Functions" in the PostgreSQL Documentation for information on TO_DATE of PostgreSQL. + - Refer to "Server Administration" > "Server Configuration" > "Setting Parameters" in the PostgreSQL Documentation for information on how to set the server parameters. + - Refer to "Date/Time Support" > "Date/Time Input Interpretation" in the PostgreSQL Documentation for information on the interpretation of existing date/time input. + +---- + +**Example** + +---- + +In the following example, the string "2016/12/31" is converted to a date and returned. + +~~~ +SELECT TO_DATE('2016/12/31','YYYY/MM/DD') FROM DUAL; + to_date +--------------------- + 2016-12-31 00:00:00 +(1 row) +~~~ + +---- + +#### 5.4.3 TO_MULTI_BYTE + +**Description** + +Converts a single-byte string to a multibyte string. + +**Syntax** + +![TO_MULTI_BYTE]( gif/TO_MULTI_BYTE.gif) + +**General rules** + + - TO_MULTI_BYTE converts halfwidth characters in string *str* to fullwidth characters, and returns the converted string. + - Only halfwidth alphanumeric characters, spaces and symbols can be converted. + - The data type of the return value is TEXT. + +**Example** + +---- + +In the following example, "abc123" is converted to fullwidth characters and returned. + +~~~ +SELECT TO_MULTI_BYTE('abc123') FROM DUAL; + to_multi_byte +--------------- + ****** +(1 row) +~~~ + +"\*\*\*\*\*\*" is multibyte "abc123". + +---- + +#### 5.4.4 TO_NUMBER + +**Description** + +Converts a value to a number in accordance with the specified format. + +**Syntax** + +![TO_NUMBER]( gif/TO_NUMBER.gif) + +**General rules** + + - TO_NUMBER converts the specified value to a numeric value in accordance with the specified format *fmt*. + - For *num*, specify a numeric data type. + - For *str*, specify a string indicating the numeric value. Numeric values must comprise only of convertible characters. + - Specify the required numeric data format. The specified numeric value is handled as is as a data type expression. + - The data type of the return value is NUMERIC. + +**See** + +---- + +Refer to "The SQL Language" > "Functions and Operators" > "Data Type Formatting Functions" in the PostgreSQL Documentation for information on numeric value formats. + +---- + +**Example** + +---- + +In the following example, the numeric literal "-130.5" is converted to a numeric value and returned. + +~~~ +SELECT TO_NUMBER(-130.5) FROM DUAL; + to_number +----------- + -130.5 +(1 row) +~~~ + +---- + +#### 5.4.5 TO_SINGLE_BYTE + +**Description** + +Converts a multibyte string to a single-byte string. + +**Syntax** + +![TO_SINGLE_BYTE]( gif/TO_SINGLE_BYTE.gif) + +**General rules** + + - TO_SINGLE_BYTE converts fullwidth characters in string *str* to halfwidth characters, and returns the converted string. + - Only fullwidth alphanumeric characters, spaces and symbols that can be displayed in halfwidth can be converted. + - The data type of the return value is TEXT. + +**Example** + +---- + +In the following example, "\*\*\*\*\*\*" is converted to halfwidth characters and returned. +"\*\*\*\*\*\*" is multibyte "xyz999". + +~~~ +SELECT TO_SINGLE_BYTE('******') FROM DUAL; + to_single_byte +---------------- + xyz999 +(1 row) +~~~ + +---- + +### 5.5 Conditional Expressions +The following functions for making comparisons are supported: + + - DECODE + - LNNVL + - NANVL + - NVL + - NVL2 + +#### 5.5.1 DECODE + +**Description** + +Compares values and if they match, returns a corresponding value. + +**Syntax** + +![DECODE]( gif/DECODE.gif) + +**General rules** + + - DECODE compares values of the value expression to be converted and the search values one by one. If the values match, a corresponding result value is returned. If no values match, the default value is returned if it has been specified. A NULL value is returned if a default value has not been specified. + - If the same search value is specified more than once, then the result value returned is the one listed for the first occurrence of the search value. + - The following data types can be used in result values and in the default value: + - CHAR + - VARCHAR + - VARCHAR2 + - NCHAR + - NCHAR VARYING + - NVARCHAR2 + - TEXT + - INTEGER + - BIGINT + - NUMERIC + - DATE + - TIME WITHOUT TIME ZONE + - TIMESTAMP WITHOUT TIME ZONE + - TIMESTAMP WITH TIME ZONE + - The same data type must be specified for the values to be converted and the search values. However, note that different data types may also be specified if a literal is specified in the search value, and the value expression to be converted contains data types that can be converted. + - If the result values and default value are all literals, the data types for these values will be as shown below: + - If all values are string literals, all will become character types. + - If there is one or more numeric literal, all will become numeric types. + - If there is one or more literal cast to the datetime/time types, all will become datetime/time types. + - If the result values and default value contain a mixture of literals and non-literals, the literals will be converted to the data types of the non-literals. + - The same data type must be specified for all result values and for the default value. However, different data types can be specified if the data type of any of the result values or default value can be converted - these data types are listed below: + + +**Data type combinations that can be converted by DECODE (summary)** + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Other result values or default value +
Numeric typeCharacter typeDate/time type
Result value (any)Numeric typeYNN
Character typeNYN
Date/time typeNNS(*1)
+ + +Y: Can be converted + +S: Some data types can be converted + +N: Cannot be converted + +*1: The data types that can be converted for date/time types are listed below: + + +**Result value and default value date/time data types that can be converted by DECODE** + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Other result values or default value +
DATETIME
WITHOUT TIME ZONE
TIMESTAMP
WITHOUT TIME ZONE
TIMESTAMP
WITH TIME ZONE
Result value (any)DATEYNYY
TIME
WITHOUT TIME ZONE
NYNN
TIMESTAMP
WITHOUT TIME ZONE
YNYY
TIMESTAMP
WITH TIME ZONE
YNYY
+ +Y: Can be converted + +N: Cannot be converted + + - The data type of the return value will be the data type within the result or default value that is longest and has the highest precision. + +**Example** + +---- + +In the following example, the value of col3 in table t1 is compared and converted to a different value. If the col3 value matches search value 1, the result value returned is "one". If the col3 value does not match any of search values 1, 2, or 3, the default value "other number" is returned. + +~~~ +SELECT col1, + DECODE(col3, 1, 'one', + 2, 'two', + 3, 'three', + 'other number') "num-word" + FROM t1; +col1 | num-word +------+---------- + 1001 | one + 1002 | two + 1003 | three +(3 rows) +~~~ + +---- + +#### 5.5.2 LNNVL + +**Description** + +Determines if a value is TRUE or FALSE for the specified condition. + +**Syntax** + +![LNNVL]( gif/LNNVL.gif) + +**General rules** + + - LNNVL determines if a value is TRUE or FALSE for the specified condition. If the result of the condition is FALSE or NULL, TRUE is returned. If the result of the condition is TRUE, FALSE is returned. + - The expression for returning TRUE or FALSE is specified in the condition. + - The data type of the return value is BOOLEAN. + +**Example** + +---- + +In the following example, col1 and col3 of table t1 are returned when col3 has a value of 2000 or less, or null values. + +~~~ +SELECT col1,col3 FROM t1 WHERE LNNVL( col3 > 2000 ); + col1 | col3 +------+------ + 1001 | 1000 + 1002 | 2000 + 2002 | +(3 row) +~~~ + +---- + +#### 5.5.3 NANVL + +**Description** + +Returns a substitute value when a value is not a number (NaN). + +**Syntax** + +![NANVL]( gif/NANVL.gif) + +**General rules** + + - NANVL returns a substitute value when the specified value is not a number (NaN). The substitute value can be either a number or a string that can be converted to a number. + - For *expr* and *substituteNum*, specify a numeric data type. If *expr* and *substituteNum* have different data types, they will be converted to the data type with greater length or precision, and that is the data type that will be returned. + - For *substituteNum*, you can also specify a string indicating the numeric value. + - The data type used for the return value if a string is specified for the substitute value will be the same as the data type of *expr*. + +**Example** + +---- + +In the following example, "0" is returned if the value of col1 in table t1 is a NaN value. + +~~~ +SELECT col1, NANVL(col3,0) FROM t1; + col1 | nanvl +------+------- + 2001 | 0 +(1 row) +~~~ + +---- + +#### 5.5.4 NVL + +**Description** + +Returns a substitute value when a value is NULL. + +**Syntax** + +![NVL]( gif/NVL.gif) + +**General rules** + + - NVL returns a substitute value when the specified value is NULL. When *expr1* is NULL, *expr2* is returned. When *expr1* is not NULL, *expr1* is returned. + - Specify the same data types for *expr1* and *expr2*. However, if a constant is specified in *expr2*, and the data type can also be converted by *expr1*, different data types can be specified. When this happens, the conversion by *expr2* is done to suit the data type in *expr1*, so the value of *expr2* returned when *expr1* is a NULL value will be the value converted in the data type of *expr1*. This is not necessary for types (numeric, int) and (bigint, int). + +**Example** + +---- + +In the following example, "IS NULL" is returned if the value of col1 in table t1 is a NULL value. + +~~~ +SELECT col2, NVL(col1,'IS NULL') "nvl" FROM t1; + col2 | nvl +------+--------- + aaa | IS NULL +(1 row) +~~~ + +---- + +#### 5.5.5 NVL2 + +**Description** + +Returns a substitute value based on whether a value is NULL or not NULL. + +**Syntax** + +![NVL2]( gif/NVL2.gif) + +**General rules** + + - NVL2 returns a substitute value based on whether the specified value is NULL or not NULL. When *expr* is NULL, *substitute2* is returned. When it is not NULL, *substitute1* is returned. + - Specify the same data types for *expr*, *substitute1*, and *substitute2*. However, if a literal is specified in *substitute1* or *substitute2*, and the data type can also be converted by *expr*, different data types can be specified. When this happens, *substitute1* or *substitute2* is converted to suit the data type in *expr*, so the value of *substitute2* returned when *expr* is a NULL value will be the value converted to the data type of *expr*. + + +**Example** + +---- + +In the following example, if a value in column col1 in table t1 is NULL, "IS NULL" is returned, and if not NULL, "IS NOT NULL" is returned. + +~~~ +SELECT col2, NVL2(col1,'IS NOT NULL','IS NULL') FROM t1; + col2 | nvl2 +------+--------- + aaa | IS NULL + bbb | IS NOT NULL +(2 row) +~~~ + +---- + +### 5.6 Aggregate Functions + +The following aggregation functions are supported: + + - LISTAGG + - MEDIAN + +#### 5.6.1 LISTAGG + +**Description** + +Returns a concatenated, delimited list of string values. + +**Syntax** + +![LISTAGG]( gif/LISTAGG.gif) + +**General rules** + + - LISTAGG concatenates and delimits a set of string values and returns the result. + - For *delimiter*, specify a string. If the delimiter is omitted, a list of strings without a delimiter is returned. + - The data type of the return value is TEXT. + +**Example** + +---- + +In the following example, the result with values of column col2 in table t1 delimited by ':' is returned. + +~~~ +SELECT LISTAGG(col2,':') FROM t1; + listagg +------------------- + AAAAA:BBBBB:CCCCC +(1 row) +~~~ + +---- + +#### 5.6.2 MEDIAN + +**Description** + +Calculates the median of a set of numbers. + +**Syntax** + +![MEDIAN]( gif/MEDIAN.gif) + +**General rules** + + - MEDIAN returns the median of a set of numbers. + - The numbers must be numeric data type. + - The data type of the return value will be REAL if the numbers are REAL type, or DOUBLE PRECISION if any other type is specified. + +**Example** + +---- + +In the following example, the median of column col3 in table t1 is returned. + +~~~ +SELECT MEDIAN(col3) FROM t1; + median +-------- + 2000 +(1 row) +~~~ + +---- + +### 5.7 Functions That Return Internal Information + +The following functions that return internal information are supported: + + - DUMP + +#### 5.7.1 DUMP + +**Description** + +Returns internal information of a value. + +**Syntax** + +![DUMP]( gif/DUMP.gif) + +**General rules** + + - DUMP returns the internal information of the values specified in expressions in a display format that is in accordance with the output format. + - The internal code (Typ) of the data type, the data length (Len) and the internal expression of the data are output as internal information. + - Any data type can be specified for the expressions. + - The display format (base *n* ) of the internal expression of the data is specified for the output format. The base numbers that can be specified are 8, 10, and 16. If omitted, 10 is used as the default. + - The data type of the return value is VARCHAR. + +**Note** + +---- + +The information output by DUMP will be the complete internal information. Therefore, the values may change due to product updates, and so on. + +---- + +**Example** + +---- + +In the following example, the internal information of column col1 in table t1 is returned. + +~~~ +SELECT col1, DUMP(col1) FROM t1; + col1 | dump +------+------------------------------------ + 1001 | Typ=25 Len=8: 32,0,0,0,49,48,48,49 + 1002 | Typ=25 Len=8: 32,0,0,0,49,48,48,50 + 1003 | Typ=25 Len=8: 32,0,0,0,49,48,48,51 +(3 row) +~~~ + +---- + +#### 5.8 Datetime Operator +The following datetime operators are supported for the DATE type of orafce. + +**Datetime operator** + +|Operation|Example|Result| +|:---:|:---|:---| +|+|DATE'2016/01/01' + 10|2016-01-11 00:00:00| +|-|DATE'2016/03/20' - 35|2016-02-14 00:00:00| +|-|DATE'2016/09/01' - DATE'2015/12/31'|245| + +**Note** + +---- + +If using datetime operators for the DATE type of orafce, it is necessary to specify "oracle" for search_path in advance. + +---- + +**See** + +---- + +Refer to "Notes on Using orafce" for information on how to edit search_path. + +---- + diff --git a/contrib/orafce/doc/orafce_documentation/Orafce_Documentation_06.md b/contrib/orafce/doc/orafce_documentation/Orafce_Documentation_06.md new file mode 100644 index 000000000..3884a9c4e --- /dev/null +++ b/contrib/orafce/doc/orafce_documentation/Orafce_Documentation_06.md @@ -0,0 +1,2000 @@ +Chapter 6 Package Reference +--- + +A "package" is a group of features, brought together by schemas, that have a single functionality, and are used by calling from PL/pgSQL. + +The following packages are supported: + + - DBMS_ALERT + - DBMS_ASSERT + - DBMS_OUTPUT + - DBMS_PIPE + - DBMS_RANDOM + - DBMS_UTILITY + - UTL_FILE + +To call the different functionalities from PL/pgSQL, use the PERFORM statement or SELECT statement, using the package name to qualify the name of the functionality. Refer to the explanations for each of the package functionalities for information on the format for calling. + + +### 6.1 DBMS_ALERT + +**Overview** + +The DBMS_ALERT package sends alerts from a PL/pgSQL session to multiple other PL/pgSQL sessions. + +This package can be used when processing 1:N, such as when notifying alerts from a given PL/pgSQL session to another PL/pgSQL session at the same time. + +**Features** + +| Feature | Description | +|:--- |:--- | +|REGISTER | Registers the specified alert.| +|REMOVE | Removes the specified alert.| +|REMOVEALL | Removes all alerts from a session.| +|SIGNAL|Notifies alerts.| +|WAITANY|Waits for notification of any alerts for which a session is registered.| +|WAITONE|Waits for notification of a specific alert for which a session is registered.| + + + +**Syntax** + +![DBMS_ALERT](gif/DBMS_ALERT.gif) + + +#### 6.1.1 Description of Features + +This section explains each feature of DBMS_ALERT. + + +**REGISTER** + + - REGISTER registers the specified alert to a session. By registering alerts to a session, SIGNAL notifications can be received. + - Specify the name of the alert. + - Alerts are case-sensitive. + - Multiple alerts can be registered within a single session. If registering multiple alerts, call REGISTER for each alert. + +**Example** + +---- + +~~~ +PERFORM DBMS_ALERT.REGISTER('sample_alert'); +~~~ + +---- + + +**REMOVE** + + - REMOVE removes the specified alert from a session. + - Specify the name of the alert. + - Alerts are case-sensitive. + - The message left by the alert will be removed. + +**Example** + +---- + +~~~ +PERFORM DBMS_ALERT.REMOVE('sample_alert'); +~~~ + +---- + + +**REMOVEALL** + + - REMOVEALL removes all alerts registered within a session. + - All messages left by the alerts will be removed. + +**Example** + +---- + +~~~ +PERFORM DBMS_ALERT.REMOVEALL(); +~~~ + +---- + + +**SIGNAL** + + - SIGNAL sends a message notification for the specified alert. + - Specify the name of the alert for which message notifications are sent. + - Alerts are case-sensitive. + - In the message, specify the alert message for notifications. + - Message notifications are not complete at the stage when SIGNAL is executed. Message notifications are sent upon committing the transaction. Message notifications are discarded if a rollback is performed after SIGNAL is executed. + - If message notifications are sent for the same alert from multiple sessions, the messages will be accumulated without being removed. + +**Example** + +---- + +~~~ +PERFORM DBMS_ALERT.SIGNAL('ALERT001','message001'); +~~~ + +---- + +**Note** + +---- + +If SIGNAL is issued continuously and the accumulated messages exceed a certain amount, an insufficient memory error may be output. If the memory becomes insufficient, call AITANY or WAITONE to receive an alert, and reduce the accumulated messages. + +---- + +**WAITANY** + + - WAITANY waits for notification of any alerts registered for a session. + - Specify the maximum wait time *timeout* in seconds to wait for an alert. + - Use a SELECT statement to obtain the notified information, which is stored in the name, message and status columns. + - The name column stores the alert names. The data type of name is TEXT. + - The message column stores the messages of notified alerts. The data type of message is TEXT. + - The status column stores the status code returned by the operation: 0-an alert occurred; 1-a timeout occurred. The data type of status is INTEGER. + + +**Example** + +---- + +~~~ +DECLARE + alert_name TEXT := 'sample_alert'; + alert_message TEXT; + alert_status INTEGER; +BEGIN + SELECT name,message,status INTO alert_name,alert_message,alert_status FROM DBMS_ALERT.WAITANY(60); +~~~ + +---- + + +**WAITONE** + + - WAITONE waits for notification of the specified alert. + - Specify the name of the alert to wait for. + - Alerts are case-sensitive. + - Specify the maximum wait time *timeout* in seconds to wait for the alert. + - Use a SELECT statement to obtain the notified information, which is stored in the message and status columns. + - The message column stores the messages of notified alerts. The data type of message is TEXT. + - The status column stores the status code returned by the operation: 0-an alert occurred; 1-a timeout occurred. The data type of status is INTEGER. + + +**Example** + +---- + +~~~ +DECLARE + alert_message TEXT; + alert_status INTEGER; +BEGIN + SELECT message,status INTO alert_message,alert_status FROM DBMS_ALERT.WAITONE('sample_alert', 60); +~~~ + +---- + + +#### 6.1.2 Usage Example +Below is a usage example of the processing flow of DBMS_ALERT. + +**DBMS_ALERT flow** + +![DBMS_ALERT_flow](gif/DBMS_ALERT_flow.gif) + + +**Note** + +---- + + - The target of message notifications by SIGNAL is sessions for which REGISTER is executed at the time of executing SIGNAL. + - On the receiving side, always ensure that REMOVE or REMOVEALL is used to remove alerts as soon as the alerts are no longer needed. If a session is closed without removing the alerts, it may no longer be possible to receive a SIGNAL for alerts of the same name in another session. + - DBMS_ALERT and DBMS_PIPE use the same memory environment. Therefore, when insufficient memory is detected for DBMS_PIPE, it is possible that insufficient memory will also be detected for DBMS_ALERT. + +---- + +**Usage example** + + - Sending side + +~~~ +CREATE FUNCTION send_dbms_alert_exe() RETURNS VOID AS $$ +BEGIN + PERFORM DBMS_ALERT.SIGNAL('sample_alert','SIGNAL ALERT'); +END; +$$ LANGUAGE plpgsql; +SELECT send_dbms_alert_exe(); +DROP FUNCTION send_dbms_alert_exe(); +~~~ + + + - Receiving side + +~~~ +CREATE FUNCTION receive_dbms_alert_exe() RETURNS VOID AS $$ +DECLARE + alert_name TEXT := 'sample_alert'; + alert_message TEXT; + alert_status INTEGER; +BEGIN + PERFORM DBMS_ALERT.REGISTER(alert_name); + SELECT message,status INTO alert_message,alert_status FROM DBMS_ALERT.WAITONE(alert_name,300); + RAISE NOTICE 'Message : %', alert_message; + RAISE NOTICE 'Status : %', alert_status; + PERFORM DBMS_ALERT.REMOVE(alert_name); +END; +$$ LANGUAGE plpgsql; +SELECT receive_dbms_alert_exe(); +DROP FUNCTION receive_dbms_alert_exe(); +~~~ + +### 6.2 DBMS_ASSERT + +**Overview** + +Performs verification of the properties of input values in PL/pgSQL. + +**Features** + +|Feature|Description| +|:---|:---| +|ENQUOTE_LITERAL|Returns the specified string enclosed in single quotation marks.| +|ENQUOTE_NAME|Returns the specified string enclosed in double quotation marks.| +|NOOP|Returns the specified string as is.| +|OBJECT_NAME|Verifies if the specified string is a defined identifier.| +|QUALIFIED_SQL_NAME|Verifies if the specified string is in the appropriate format as an identifier.| +|SCHEMA_NAME|Verifies if the specified string is a defined schema.| +|SIMPLE_SQL_NAME|Verifies if the specified string is in the appropriate format as a single identifier.| + + +**Syntax** + +![DBMS_ASSERT](gif/DBMS_ASSERT.gif) + +#### 6.2.1 Description of Features + +This section explains each feature of DBMS_ASSERT. + +**ENQUOTE_LITERAL** + + - ENQUOTE_LITERAL returns the specified string enclosed in single quotation marks. + - Specify a string enclosed in single quotation marks. + - The data type of the return value is VARCHAR. + +**Example** + +---- + +~~~ +DECLARE + q_literal VARCHAR(256); +BEGIN + q_literal := DBMS_ASSERT.ENQUOTE_LITERAL('literal_word'); +~~~ + +---- + + +**ENQUOTE_NAME** + + - ENQUOTE_NAME returns the specified string enclosed in double quotation marks. + - Specify a string enclosed in double quotation marks. + - For lowercase conversion, specify TRUE or FALSE. Specify TRUE to convert uppercase characters in the string to lowercase. If FALSE is specified, conversion to lowercase will not take place. The default is TRUE. + - If all the characters in the string are lowercase, they will not be enclosed in double quotation marks. + - The data type of the return value is VARCHAR. + +**See** + +---- + +Refer to "The SQL Language" > "Data Types" > "Boolean Type" in the PostgreSQL Documentation for information on boolean type (TRUE/FALSE) values. + +---- + +**Example** + +---- + +~~~ +DECLARE + dq_literal VARCHAR(256); +BEGIN + dq_literal := DBMS_ASSERT.ENQUOTE_NAME('TBL001'); +~~~ + +---- + +**NOOP** + + - NOOP returns the specified string as is. + - Specify a string. + - The data type of the return value is VARCHAR. + + +**Example** + +---- + +~~~ +DECLARE + literal VARCHAR(256); +BEGIN + literal := DBMS_ASSERT.NOOP('NOOP_WORD'); +~~~ + +---- + + +**OBJECT_NAME** + + - OBJECT_NAME verifies if the specified string is a defined identifier. + - Specify the identifier for verification. If the identifier has been defined, the specified identifier will be returned. Otherwise, the following error will occur. + +~~~ +ERROR: invalid object name +~~~ + + - The data type of the return value is VARCHAR. + + +**Example** + +---- + +~~~ +DECLARE + object_name VARCHAR(256); +BEGIN + object_name := DBMS_ASSERT.OBJECT_NAME('SCM001.TBL001'); +~~~ + +---- + + +**QUALIFIED_SQL_NAME** + + - QUALIFIED_SQL_NAME verifies if the specified string is in the appropriate format as an identifier. + - Specify the identifier for verification. If the string can be used as an identifier, the specified identifier will be returned. Otherwise, the following error will occur. + +~~~ +ERROR: string is not qualified SQL name +~~~ + + - The data type of the return value is VARCHAR. + +**See** + +---- + +Refer to "The SQL Language" > "Lexical Structure" > "Identifiers and Key Words" in the PostgreSQL Documentation for information on the formats that can be used as identifiers. + +---- + +**Example** + +---- + +~~~ +DECLARE + object_name VARCHAR(256); +BEGIN + object_name := DBMS_ASSERT.QUALIFIED_SQL_NAME('SCM002.TBL001'); +~~~ + +---- + +**SCHEMA_NAME** + + - SCHEMA_NAME verifies if the specified string is a defined schema. + - Specify a schema name for verification. If the schema has been defined, the specified schema name will be returned. Otherwise, the following error will occur. + +~~~ +ERROR: invalid schema name +~~~ + + - The data type of the return value is VARCHAR. + + +**Example** + +---- + +~~~ +DECLARE + schema_name VARCHAR(256); +BEGIN + schema_name := DBMS_ASSERT.SCHEMA_NAME('SCM001'); +~~~ + +---- + + +**SIMPLE_SQL_NAME** + + - SIMPLE_SQL_NAME verifies if the specified string is in the appropriate format as a single identifier. + - Specify an identifier for verification. If the specified string can be used as an identifier, the specified identifier will be returned. Otherwise, the following error will occur. + +~~~ +ERROR: string is not qualified SQL name +~~~ + + - The data type of the return value is VARCHAR. + + +**See** + +---- + +Refer to "The SQL Language" > "Lexical Structure" > "Identifiers and Key Words" in the PostgreSQL Documentation for information on the formats that can be used as identifiers. Note that an error will occur if an identifier using fullwidth characters is specified. If fullwidth characters are included, specify a quoted identifier. + +---- + +**Example** + +---- + +~~~ +DECLARE + simple_name VARCHAR(256); +BEGIN + simple_name := DBMS_ASSERT.SIMPLE_SQL_NAME('COL01'); +~~~ + +---- + +#### 6.2.2 Usage Example +A usage example of DBMS_ASSERT is shown below. +~~~ +CREATE FUNCTION dbms_assert_exe() RETURNS VOID AS $$ +DECLARE + w_schema VARCHAR(20) := 'public'; + w_table VARCHAR(20) := 'T1'; + w_object VARCHAR(40); +BEGIN + PERFORM DBMS_ASSERT.NOOP(w_schema); + PERFORM DBMS_ASSERT.SIMPLE_SQL_NAME(w_table); + PERFORM DBMS_ASSERT.SCHEMA_NAME(w_schema); + w_object := w_schema || '.' || w_table; + PERFORM DBMS_ASSERT.QUALIFIED_SQL_NAME(w_object); + PERFORM DBMS_ASSERT.OBJECT_NAME(w_object); + RAISE NOTICE 'OBJECT : %', DBMS_ASSERT.ENQUOTE_LITERAL(w_object); + RAISE NOTICE 'TABLE_NAME : %', DBMS_ASSERT.ENQUOTE_NAME(w_table); +END; +$$ +LANGUAGE plpgsql; +SELECT dbms_assert_exe(); +DROP FUNCTION dbms_assert_exe(); +~~~ + + + +### 6.3 DBMS_OUTPUT + +**Overview** + +Sends messages to clients such as psql from PL/pgSQL. + + +**Features** + +|Feature|Description| +|:---|:---| +|ENABLE|Enables features of this package.| +|DISABLE|Disables features of this package.| +|SERVEROUTPUT|Controls whether messages are sent.| +|PUT|Sends messages.| +|PUT_LINE|Sends messages with a newline character appended.| +|NEW_LINE|Sends a newline character.| +|GET_LINE|Retrieves a line from the message buffer.| +|GET_LINES|Retrieves multiple lines from the message buffer.| + + +**Syntax** + +![DBMS_OUTPUT](gif/DBMS_OUTPUT.gif) + +#### 6.3.1 Description +This section explains each feature of DBMS_OUTPUT. + +**ENABLE** + + - ENABLE enables the use of PUT, PUT_LINE, NEW_LINE, GET_LINE, and GET_LINES. + - With multiple executions of ENABLE, the value specified last is the buffer size (in bytes). Specify a buffer size from 2000 to 1000000. + - The default value of the buffer size is 20000. If NULL is specified as the buffer size, 1000000 will be used. + - If ENABLE has not been executed, PUT, PUT_LINE, NEW_LINE, GET_LINE, and GET_LINES are ignored even if they are executed. + + +**Example** + +---- + +~~~ +PERFORM DBMS_OUTPUT.ENABLE(20000); +~~~ + +---- + + +**DISABLE** + + - DISABLE disables the use of PUT, PUT_LINE, NEW_LINE, GET_LINE, and GET_LINES. + - Remaining buffer information is discarded. + + +**Example** + +---- + +~~~ +PERFORM DBMS_OUTPUT.DISABLE(); +~~~ + +---- + +**SERVEROUTPUT** + + - SERVEROUTPUT controls whether messages are sent. + - Specify TRUE or FALSE for *sendMsgs*. + - If TRUE is specified, when PUT, PUT_LINE, or NEW_LINE is executed, the message is sent to a client such as psql and not stored in the buffer. + - If FALSE is specified, when PUT, PUT_LINE, or NEW_LINE is executed, the message is stored in the buffer and not sent to a client such as psql. + + +**See** + +---- + +Refer to "The SQL Language" > "Data Types" > "Boolean Type" in the PostgreSQL Documentation for information on boolean type (TRUE/FALSE) values. + +---- + +**Example** + +---- + +~~~ +PERFORM DBMS_OUTPUT.SERVEROUTPUT(TRUE); +~~~ + +---- + + +**PUT** + + - PUT sets the message to be sent. + - The string is the message to be sent. + - When TRUE is specified for SERVEROUTPUT, the messages are sent to clients such as psql. + - When FALSE is specified for SERVEROUTPUT, the messages are retained in the buffer. + - PUT does not append a newline character. To append a newline character, execute NEW_LINE. + - If a string longer than the buffer size specified in ENABLE is sent, an error occurs. + +**Example** + +---- + +~~~ +PERFORM DBMS_OUTPUT.PUT('abc'); +~~~ + +---- + + +**PUT_LINE** + + - PUT_LINE sets the message to be sent appended with a newline character. + - The string is the message to be sent. + - When TRUE is specified for SERVEROUTPUT, the messages are sent to clients such as psql. + - When FALSE is specified for SERVEROUTPUT, the messages are retained in the buffer. + - If a string longer than the buffer size specified in ENABLE is sent, an error occurs. + + +**Example** + +---- + +~~~ +PERFORM DBMS_OUTPUT.PUT_LINE('abc'); +~~~ + +---- + + +**NEW_LINE** + + - NEW_LINE appends a newline character to the message created with PUT. + - When TRUE is specified for SERVEROUTPUT, the messages are sent to clients such as psql. + - When FALSE is specified for SERVEROUTPUT, the messages are retained in the buffer. + +**Example** + +---- + +~~~ +PERFORM DBMS_OUTPUT.NEW_LINE(); +~~~ + +---- + +**GET_LINE** + + - GET_LINE retrieves a line from the message buffer. + - Use a SELECT statement to obtain the retrieved line and status code returned by the operation, which are stored in the line and status columns. + - The line column stores the line retrieved from the buffer. The data type of line is TEXT. + - The status column stores the status code returned by the operation: 0-completed successfully; 1-failed because there are no more lines in the buffer. The data type of status is INTEGER. + - If GET_LINE or GET_LINES is executed and then PUT, PUT_LINE or PUT_LINES is executed while messages that have not been retrieved from the buffer still exist, the messages not retrieved from the buffer will be discarded. + + +**Example** + +---- + +~~~ +DECLARE + buff1 VARCHAR(20); + stts1 INTEGER; +BEGIN + SELECT line,status INTO buff1,stts1 FROM DBMS_OUTPUT.GET_LINE(); +~~~ + +---- + +**GET_LINES** + + - GET_LINES retrieves multiple lines from the message buffer. + - Specify the number of lines to retrieve from the buffer. + - Use a SELECT statement to obtain the retrieved lines and the number of lines retrieved, which are stored in the lines and numlines columns. + - The lines column stores the lines retrieved from the buffer. The data type of lines is TEXT. + - The numlines column stores the number of lines retrieved from the buffer. If this number is less than the number of lines requested, then there are no more lines in the buffer. The data type of numlines is INTEGER. + - If GET_LINE or GET_LINES is executed and then PUT, PUT_LINE, or NEW_LINE is executed while messages that have not been retrieved from the buffer still exist, the messages not retrieved from the buffer will be discarded. + + +**Example** + +---- + +~~~ +DECLARE + buff VARCHAR(20)[10]; + stts INTEGER := 10; +BEGIN + SELECT lines, numlines INTO buff,stts FROM DBMS_OUTPUT.GET_LINES(stts); +~~~ + +---- + + +#### 6.3.2 Usage Example + +A usage example of DBMS_OUTPUT is shown below. + +~~~ +CREATE FUNCTION dbms_output_exe() RETURNS VOID AS $$ +DECLARE + buff1 VARCHAR(20); + buff2 VARCHAR(20); + stts1 INTEGER; + stts2 INTEGER; +BEGIN + PERFORM DBMS_OUTPUT.DISABLE(); + PERFORM DBMS_OUTPUT.ENABLE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT(FALSE); + PERFORM DBMS_OUTPUT.PUT('DBMS_OUTPUT TEST 1'); + PERFORM DBMS_OUTPUT.NEW_LINE(); + PERFORM DBMS_OUTPUT.PUT_LINE('DBMS_OUTPUT TEST 2'); + SELECT line,status INTO buff1,stts1 FROM DBMS_OUTPUT.GET_LINE(); + SELECT line,status INTO buff2,stts2 FROM DBMS_OUTPUT.GET_LINE(); + PERFORM DBMS_OUTPUT.SERVEROUTPUT(TRUE); + PERFORM DBMS_OUTPUT.PUT_LINE(buff1); + PERFORM DBMS_OUTPUT.PUT_LINE(buff2); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_output_exe(); +DROP FUNCTION dbms_output_exe(); +~~~ + + +### 6.4 DBMS_PIPE + +**Overview** + +Performs communication between sessions that execute PL/pgSQL. + +This package can be used for 1:1 communication, such as when data is being exchanged between sessions executing PL/pgSQL. + +For pipes, there are explicit pipes and implicit pipes, and furthermore, for explicit pipes, you can select public pipes and private pipes. The characteristics of each type are as follows: + + +**Types of pipes** + +|Type|Characteristics| +|:---|:---| +|Explicit pipe|- CREATE_PIPE is used to create a pipe explicitly.
- While creating a pipe,
you can select between a public pipe and private pipe.
- It is necessary to use REMOVE_PIPE to explicitly remove a pipe.| +|Implicit pipe|- Created automatically when SEND_MESSAGE and RECEIVE_MESSAGE are used.
- The pipe that is created becomes a public pipe.
- When messages are received using RECEIVE_MESSAGE,
if there are no additional messages remaining in the pipe,
the pipe will be removed automatically.| +|Public pipe|- Can be created as an explicit pipe or implicit pipe.
- Can also be used by users other than the creator.| +|Private pipe|- Can only be created as an explicit pipe.
- Can only be used by its creator.| + +**Note** + +---- + + - Up to 50 pipes can be used concurrently by a single instance. + - In cases where pipes are frequently created and removed repetitively, use public pipes. If you create a private pipe, internal information (the creator of the private pipe) will remain even after the pipe is removed. Thus, repeatedly creating and removing pipes may ultimately cause memory to run out. + - If a timeout occurs without receiving a message when an implicit pipe is created by RECEIVE_MESSAGE, the pipe will not be removed. + +---- + +**Features** + +|Feature|Description| +|:---|:---| +|CREATE_PIPE|Creates a public or private pipe.| +|NEXT_ITEM_TYPE|Determines the data type of the next item in the local buffer, and returns that type.| +|PACK_MESSAGE|Sets a message in the local buffer.| +|PURGE|Empties the contents of the specified pipe.| +|RECEIVE_MESSAGE|Sets a received message in the local buffer.| +|REMOVE_PIPE|Removes the specified pipe.| +|RESET_BUFFER|Resets the set position of the local buffer.| +|SEND_MESSAGE|Sends the contents of the local buffer.| +|UNIQUE_SESSION_NAME|Returns a unique session name.| +|UNPACK_MESSAGE_BYTEA|Receives a message in the local buffer in BYTEA type.| +|UNPACK_MESSAGE_DATE|Receives a message in the local buffer in DATE type.| +|UNPACK_MESSAGE_NUMBER|Receives a message in the local buffer in NUMERIC type.| +|UNPACK_MESSAGE_RECORD|Receives a message in the local buffer in RECORD type.| +|UNPACK_MESSAGE_TEXT|Receives a message in the local buffer in TEXT type.| +|UNPACK_MESSAGE_TIMESTAMP|Receives a message in the local buffer in TIMESTAMP type.| + + +**Syntax** + +![DBMS_PIPE](gif/DBMS_PIPE.gif) + +#### 6.4.1 Description of Features + +This section explains each feature of DBMS_PIPE. + +**CREATE_PIPE** + + - CREATE_PIPE explicitly creates a pipe environment for data communication. + - Specify the name of the pipe to be created. + - Pipe names are case-sensitive. + - Specify the maximum number of messages that can be sent or received. If omitted, 0 (cannot send messages) will be used. Specify from 1 to 32767. + - Specify TRUE or FALSE for *private*. If TRUE is specified, a private pipe will be created. If FALSE is specified, a public pipe will be created. The default is FALSE. + - An error will occur if a pipe of the same name has already been created. + +**See** + +---- + +Refer to "The SQL Language" > "Data Types" > "Boolean Type" in the PostgreSQL Documentation for information on boolean type (TRUE/FALSE) values. + +--- + +**Example** + +---- + +~~~ +PERFORM DBMS_PIPE.CREATE_PIPE('P01', 100, FALSE); +~~~ + +---- + + +**NEXT_ITEM_TYPE** + + - NEXT_ITEM_TYPE returns the next data type in the local buffer. + - The data type of the return value is INTEGER. One of the following values is returned: + + +**Values returned by NEXT_ITEM_TYPE** + +|Return value|Data type| +|:---|:---| +|9|NUMERIC type| +|11|TEXT type| +|12|DATE type| +|13|TIMESTAMP type| +|23|BYTEA type| +|24|RECORD type| +|0|No data in the buffer| + +**Example** + +---- + +~~~ +DECLARE + i_iType INTEGER; +BEGIN + i_iType := DBMS_PIPE.NEXT_ITEM_TYPE(); +~~~ + +---- + +**PACK_MESSAGE** + + - PACK_MESSAGE sets the specified message in the local buffer. + - Specify the data to be set in the local buffer. The following data types can be used: + - Character type (\*1) + - Integer type (\*2) + - NUMERIC type + - DATE type + - TIMESTAMP type (\*3) + - BYTEA type + - RECORD type + +\*1: The character type is converted internally to TEXT type. + +\*2: The integer type is converted internally to NUMERIC type. + +\*3: The TIMESTAMP type is converted internally to TIMESTAMP WITH TIME ZONE type. + + - Each time PACK_MESSAGE is called, a new message is added to the local buffer. + - The size of the local buffer is approximately 8 KB. However, each message has overhead, so the total size that can be stored is actually less than 8 KB. To clear the local buffer, send a message (SEND_MESSAGE), or reset the buffer (RESET_BUFFER) to its initial state. + +**Example** + +---- + +~~~ +PERFORM DBMS_PIPE.PACK_MESSAGE('Message Test001'); +~~~ + +---- + + +**PURGE** + + - PURGE removes the messages in the pipe. + - Specify the name of the pipe for which the messages are to be removed. + - Pipe names are case-sensitive. + +**Example** + +---- + +~~~ +PERFORM DBMS_PIPE.PURGE('P01'); +~~~ + +---- + +**Note** + +---- + +When PURGE is executed, the local buffer is used to remove the messages in the pipe. Therefore, if there are any messages remaining in the pipe, the local buffer will be overwritten by PURGE. + +---- + +**RECEIVE_MESSAGE** + + - RECEIVE_MESSAGE receives messages that exist in the specified pipe, and sets those messages in the local buffer. + - Messages are received in the units in which they are sent to the pipe by SEND_MESSAGE. Received messages are removed from the pipe after being set in the local buffer. + - Specify the name of the pipe for which the messages are to be received. + - Pipe names are case-sensitive. + - Specify the maximum wait time *timeout* in seconds to wait for a message. If omitted, the default is 31536000 seconds (1 year). + - The data type of the return value is INTEGER. If a message is received successfully, 0 is returned. If a timeout occurs, 1 is returned. + +**Example** + +---- + +~~~ +DECLARE + i_Ret INTEGER; +BEGIN + i_Ret := DBMS_PIPE.RECEIVE_MESSAGE('P01', 60); +~~~ + +---- + +**REMOVE_PIPE** + + - REMOVE_PIPE removes the specified pipe. + - Specify the name of the pipe to be removed. + - Pipe names are case-sensitive. + + +**Example** + +---- + +~~~ +PERFORM DBMS_PIPE.REMOVE_PIPE('P01'); +~~~ + +---- + +**RESET_BUFFER** + + - RESET_BUFFER resets the set position of the local buffer. Any unnecessary data remaining in the local buffer can be discarded using this operation. + + +**Example** + +---- + +~~~ +PERFORM DBMS_PIPE.RESET_BUFFER(); +~~~ + +---- + + +**SEND_MESSAGE** + + - SEND_MESSAGE sends data stored in the local buffer to the specified pipe. + - Specify the name of the pipe that the data is to be sent to. + - Pipe names are case-sensitive. + - Specify the maximum wait time *timeout* in seconds for sending data stored in the local buffer. If omitted, the default is 31536000 seconds (1 year). + - Specify the maximum number of messages that can be sent or received. If omitted, the maximum number of messages set in CREATE_PIPE is used. If omitted in the implicit pipe, the number of messages will be unlimited. Specify from 1 to 32767. + - If the maximum number of messages is specified in both SEND_MESSAGE and CREATE_PIPE, the larger of the values will be used. + - The data type of the return value is INTEGER. If a message is received successfully, 0 is returned. If a timeout occurs, 1 is returned. + + +**Example** + +---- + +~~~ +DECLARE + i_Ret INTEGER; +BEGIN + i_Ret := DBMS_PIPE.SEND_MESSAGE('P01', 10, 20); +~~~ + +---- + +**Note** + +---- + +A timeout will occur during sending if the maximum number of messages is reached, or if the message being sent is too large. If a timeout occurs, use RECEIVE_MESSAGE to receive any messages that are in the pipe. + +---- + +**UNIQUE_SESSION_NAME** + + - UNIQUE_SESSION_NAME returns a name that is unique among all the sessions. This name can be used as the pipe name. + - Multiple calls from the same session always return the same name. + - The data type of the return value is VARCHAR. Returns a string of up to 30 characters. + + +**Example** + +---- + +~~~ +DECLARE + p_Name VARCHAR(30); +BEGIN + p_Name := DBMS_PIPE.UNIQUE_SESSION_NAME(); +~~~ + +---- + + +**UNPACK_MESSAGE_BYTEA** + + - NPACK_MESSAGE_BYTEA receives BTYEA type messages in the local buffer. + - Messages are received in the unit set in the local buffer by PACK_MESSAGE. Received messages are removed from the local buffer. + - The data type of the return value is BYTEA. + - If no messages exist in the local buffer, a NULL value is returned. + - For the data type, it is necessary to align with the data type set by PACK_MESSAGE. If the data type is different, the following error will occur. + +~~~ +ERROR: datatype mismatch +DETAIL: unpack unexpected type: xx +~~~ + +**Example** + +---- + +~~~ +DECLARE + g_Bytea BYTEA; +BEGIN + g_Bytea := DBMS_PIPE.UNPACK_MESSAGE_BYTEA(); +~~~ + +---- + + +**UNPACK_MESSAGE_DATE** + + - UNPACK_MESSAGE_DATE receives DATE type messages in the local buffer. + - Messages are received in the unit set in the local buffer by PACK_MESSAGE. Received messages are removed from the local buffer. + - The data type of the return value is DATE. + - If no messages exist in the local buffer, a NULL value is returned. + - For the data type, it is necessary to align with the data type set by PACK_MESSAGE. If the data type is different, the following error will occur. + +~~~ +ERROR: datatype mismatch +DETAIL: unpack unexpected type: xx +~~~ + +**Example** + +---- + +~~~ +DECLARE + g_Date DATE; +BEGIN + g_Date := DBMS_PIPE.UNPACK_MESSAGE_DATE(); +~~~ + +---- + +**Note** + +---- + +If the "oracle" schema is set in search_path, the DATE type of orafce will be used, so for receiving data, use UNPACK_MESSAGE_TIMESTAMP. UNPACK_MESSAGE_DATE is the interface for the DATE type of PostgreSQL. + +---- + + +**UNPACK_MESSAGE_NUMBER** + + - UNPACK_MESSAGE_NUMBER receives NUMERIC type messages in the local buffer. + - Messages are received in the unit set in the local buffer by PACK_MESSAGE. Received messages are removed from the local buffer. + - The data type of the return value is NUMERIC. + - If no messages exist in the local buffer, a NULL value is returned. + - For the data type, it is necessary to align with the data type set by PACK_MESSAGE. If the data type is different, the following error will occur. + +~~~ +ERROR: datatype mismatch +DETAIL: unpack unexpected type: xx +~~~ + +**Example** + +---- + +~~~ +DECLARE + g_Number NUMERIC; +BEGIN + g_Number := DBMS_PIPE.UNPACK_MESSAGE_NUMBER(); +~~~ + +---- + +**UNPACK_MESSAGE_RECORD** + + - UNPACK_MESSAGE_RECORD receives RECORD type messages in the local buffer. + - Messages are received in the unit set in the local buffer by PACK_MESSAGE. Received messages are removed from the local buffer. + - The data type of the return value is RECORD. + - If no messages exist in the local buffer, a NULL value is returned. + - For the data type, it is necessary to align with the data type set by PACK_MESSAGE. If the data type is different, the following error will occur. + +~~~ +ERROR: datatype mismatch +DETAIL: unpack unexpected type: xx +~~~ + +**Example** + +---- + +~~~ +DECLARE + msg1 TEXT; + status NUMERIC; +BEGIN + SELECT col1, col2 INTO msg1, status FROM DBMS_PIPE.UNPACK_MESSAGE_RECORD(); +~~~ + +---- + + +**UNPACK_MESSAGE_TEXT** + + - UNPACK_MESSAGE_TEXT receives TEXT type messages in the local buffer. + - Messages are received in the unit set in the local buffer by PACK_MESSAGE. Received messages are removed from the local buffer. + - The data type of the return value is TEXT. + - If no messages exist in the local buffer, a NULL value is returned. + - For the data type, it is necessary to align with the data type set by PACK_MESSAGE. If the data type is different, the following error will occur. + +~~~ +ERROR: datatype mismatch +DETAIL: unpack unexpected type: xx +~~~ + +**Example** + +---- + +~~~ +DECLARE + g_Text TEXT; +BEGIN + g_Text := DBMS_PIPE.UNPACK_MESSAGE_TEXT(); +~~~ + +---- + + +**UNPACK_MESSAGE_TIMESTAMP** + + - UNPACK_MESSAGE_TIMESTAMP receives TIMESTAMP WITH TIME ZONE type messages in the local buffer. + - Messages are received in the unit set in the local buffer by PACK_MESSAGE. Received messages are removed from the local buffer. + - The data type of the return value is TIMESTAMP WITH TIME ZONE. + - If no messages exist in the local buffer, a NULL value is returned. + - For the data type, it is necessary to align with the data type set by PACK_MESSAGE. If the data type is different, the following error will occur. + +~~~ +ERROR: datatype mismatch +DETAIL: unpack unexpected type: xx +~~~ + + +**Example** + +---- + +~~~ +DECLARE + g_Timestamptz TIMESTAMP WITH TIME ZONE; +BEGIN + g_Timestamptz := DBMS_PIPE.UNPACK_MESSAGE_TIMESTAMP(); +~~~ + +---- + + +#### 6.4.2 Usage Example +Below is a usage example of the processing flow of DBMS_PIPE. + +**Flow of DBMS_PIPE** + +![DBMS_PIPE_flow](gif/DBMS_PIPE_flow.gif) + + + + + + + + +**Note** + +---- + + - When CREATE_PIPE is used to explicitly create a pipe, ensure to use REMOVE_PIPE to remove the pipe. If a pipe is not removed explicitly, once created, it will remain until the instance is stopped. + - In the flow diagram, CREATE_PIPE and REMOVE_PIPE are described on the receiving side, however, these can be executed on the sending side. In order to maintain consistency, it is recommended to create and remove pipes on one side. + - An error will occur for CREATE_PIPE if a pipe of the same name already exists. Implicitly created pipes are also the target of SEND_MESSAGE and RECEIVE_MESSAGE, so when executing CREATE_PIPE, ensure that SEND_MESSAGE and RECEIVE_MESSAGE are not called beforehand. + - DBMS_ALERT and DBMS_PIPE use the same memory environment. Therefore, when insufficient memory is detected for DBMS_ALERT, it is possible that insufficient memory will also be detected for DBMS_PIPE. + +---- + + +**Information** + +---- + +The information of pipes that are in use can be viewed in the DBMS_PIPE.DB_PIPES view. + +~~~ +SELECT * from dbms_pipe.db_pipes; + name | items | size | limit | private | owner +------+-------+------+-------+---------+------- + P01 | 1 | 18 | 100 | f | +(1 row) +~~~ + +---- + + + +**Usage example** + + - Sending side + +~~~ +CREATE FUNCTION send_dbms_pipe_exe(IN pipe_mess text) RETURNS void AS $$ +DECLARE + pipe_name text := 'sample_pipe'; + pipe_time timestamp := current_timestamp; + pipe_stat int; +BEGIN + PERFORM DBMS_PIPE.RESET_BUFFER(); + PERFORM DBMS_PIPE.PACK_MESSAGE(pipe_mess); + PERFORM DBMS_PIPE.PACK_MESSAGE(pipe_time); + pipe_stat := DBMS_PIPE.SEND_MESSAGE(pipe_name); + RAISE NOTICE 'PIPE_NAME: % SEND Return Value =%', pipe_name, pipe_stat; +END; +$$ LANGUAGE plpgsql; + +SELECT send_dbms_pipe_exe('Sample Message.'); +DROP FUNCTION send_dbms_pipe_exe(text); +~~~ + + + + - Receiving side + +~~~ +CREATE FUNCTION receive_dbms_pipe_exe() RETURNS void AS $$ +DECLARE + pipe_name text := 'sample_pipe'; + pipe_text text; + pipe_nume numeric; + pipe_date date; + pipe_time timestamp with time zone; + pipe_byte bytea; + pipe_reco record; + pipe_item int; + pipe_stat int; +BEGIN + pipe_stat := DBMS_PIPE.RECEIVE_MESSAGE(pipe_name,300); + RAISE NOTICE 'Return Value = %', pipe_stat; + LOOP + pipe_item := DBMS_PIPE.NEXT_ITEM_TYPE(); + RAISE NOTICE 'Next Item : %', pipe_item; + IF (pipe_item = 9) THEN + pipe_nume := DBMS_PIPE.UNPACK_MESSAGE_NUMBER(); + RAISE NOTICE 'Get Message : %' ,pipe_nume; + ELSIF (pipe_item =11) THEN + pipe_text := DBMS_PIPE.UNPACK_MESSAGE_TEXT(); + RAISE NOTICE 'Get Message : %' ,pipe_text; + ELSIF (pipe_item = 12) THEN + pipe_date := DBMS_PIPE.UNPACK_MESSAGE_DATE(); + RAISE NOTICE 'Get Message : %' ,pipe_date; + ELSIF (pipe_item = 13) THEN + pipe_time := DBMS_PIPE.UNPACK_MESSAGE_TIMESTAMP(); + RAISE NOTICE 'Get Message : %' ,pipe_time; + ELSIF (pipe_item = 23) THEN + pipe_byte := DBMS_PIPE.UNPACK_MESSAGE_BYTEA(); + RAISE NOTICE 'Get Message : %' ,pipe_byte; + ELSIF (pipe_item = 24) THEN + pipe_reco := DBMS_PIPE.UNPACK_MESSAGE_RECORD(); + RAISE NOTICE 'Get Message : %' ,pipe_reco; + ELSE + EXIT; + END IF; + END LOOP; + PERFORM DBMS_PIPE.REMOVE_PIPE(pipe_name); +END; +$$ LANGUAGE plpgsql; + +SELECT receive_dbms_pipe_exe(); +DROP FUNCTION receive_dbms_pipe_exe(); +~~~ + + +### 6.5 DBMS_RANDOM + +**Overview** + +Generates random numbers in PL/pgSQL. + +**Features** + +|Feature|Description| +|:---|:---| +|INITIALIZE|Initializes the generation of random numbers.| +|NORMAL|Returns a normally distributed random number.| +|RANDOM|Generates a random number.| +|SEED|Resets the seed value.| +|STRING|Generates a random string.| +|TERMINATE|Terminates generation of random numbers.| +|VALUE|Generates a random decimal number between 0 and 1, or between specified values.| + + +**Syntax** + +![DBMS_RANDOM](gif/DBMS_RANDOM.gif) + + +#### 6.5.1 Description of Features + +This section explains each feature of DBMS_RANDOM. + +**INITIALIZE** + + - INITIALIZE initializes the generation of random numbers using the specified seed value. + - For *seedVal*, specify a SMALLINT or INTEGER type. + +**Example** + +---- + +~~~ +PERFORM DBMS_RANDOM.INITIALIZE(999); +~~~ + +---- + +**NORMAL** + + - NORMAL generates and returns a normally distributed random number. + - The return value type is DOUBLE PRECISION. + + +**Example** + +---- + +~~~ +DECLARE + d_RunNum DOUBLE PRECISION; +BEGIN + d_RunNum := DBMS_RANDOM.NORMAL(); +~~~ + +---- + + +**RANDOM** + + - RANDOM generates and returns a random number. + - The data type of the return value is INTEGER. + + +**Example** + +---- + +~~~ +DECLARE + d_RunInt INTEGER; +BEGIN + d_RunInt := DBMS_RANDOM.RANDOM(); +~~~ + +---- + +**SEED** + + - SEED initializes the generation of a random number using the specified seed value or seed string. + - For *seedVal*, specify a SMALLINT or INTEGER type. + - Any string can be specified for the seed string. + + +**Example** + +---- + +~~~ +PERFORM DBMS_RANDOM.SEED('123'); +~~~ + +---- + + +**STRING** + + - STRING generates and returns a random string in accordance with the specified display format and string length. + - For the display format *fmt*, specify any of the following values. An error will occur if any other value is specified. + + +**Values that can be specified for the display format** + +|Setting value|Generated string| +|:---|:---| +|'u', 'U'|Uppercase letters only| +|'l', 'L'|Lowercase letters only| +|'a', 'A'|Mixture of uppercase and lowercase letters| +|'x', 'X'|Uppercase letters and numbers| +|'p', 'P'|Any displayable character| + + - Specify the length of the string to be generated. Specify a SMALLINT or INTEGER type. + - The data type of the return value is TEXT. + + + +**Example** + +---- + +~~~ +DECLARE + d_RunStr TEXT; +BEGIN + d_RunStr := DBMS_RANDOM.STRING('a', 20); +~~~ + +---- + + +**TERMINATE** + + - Call TERMINATE to terminate generation of random numbers. + + +**Information** + +TERMINATE does not do anything, but has been included for compatibility with Oracle databases. + +**Example** + +---- + +~~~ +PERFORM DBMS_RANDOM.TERMINATE(); +~~~ + +---- + +**VALUE** + + - VALUE generates and returns a random number within the specified range. + - For *min* and *max*, specify a numeric data type. A random number between and inclusive of the minimum value and maximum value is generated. + - If the minimum value and maximum value are omitted, a random decimal number between 0 and 1 will be generated. + - The data type of the return value is DOUBLE PRECISION. + + +**Example** + +---- + +~~~ +DECLARE + d_RunDbl DOUBLE PRECISION; +BEGIN + d_RunDbl := DBMS_RANDOM.VALUE(); +~~~ + +---- + + +#### 6.5.2 Usage Example + +A usage example of DBMS_RANDOM is shown below. + +~~~ +CREATE FUNCTION dbms_random_exe() RETURNS VOID AS $$ +DECLARE + w_rkey VARCHAR(10) := 'rnd111'; + i_rkey INTEGER := 97310; +BEGIN + PERFORM DBMS_RANDOM.INITIALIZE(i_rkey); + RAISE NOTICE 'RANDOM -> NORMAL : %', DBMS_RANDOM.NORMAL(); + RAISE NOTICE 'RANDOM -> RANDOM : %', DBMS_RANDOM.RANDOM(); + RAISE NOTICE 'RANDOM -> STRING : %', DBMS_RANDOM.STRING('a',10); + RAISE NOTICE 'RANDOM -> VALUE : %', DBMS_RANDOM.VALUE(); + PERFORM DBMS_RANDOM.SEED(w_rkey); + RAISE NOTICE 'RANDOM -> NORMAL : %', DBMS_RANDOM.NORMAL(); + RAISE NOTICE 'RANDOM -> RANDOM : %', DBMS_RANDOM.RANDOM(); + RAISE NOTICE 'RANDOM -> STRING : %', DBMS_RANDOM.STRING('p',10); + RAISE NOTICE 'RANDOM -> VALUE : %', DBMS_RANDOM.VALUE(1,100); + PERFORM DBMS_RANDOM.TERMINATE(); +END; +$$ LANGUAGE plpgsql; +SELECT dbms_random_exe(); +DROP FUNCTION dbms_random_exe(); +~~~ + +### 6.6 DBMS_UTILITY + +**Overview** + +Provides utilities of PL/pgSQL. + + +**Features** + +|Feature|Description| +|:---|:---| +|FORMAT_CALL_STACK|Returns the current call stack.| +|GET_TIME|Returns the number of hundredths of seconds that have elapsed since a point in time in the past.| + + +**Syntax** + +![DBMS_UTILITY](gif/DBMS_UTILITY.gif) + +#### 6.6.1 Description of Features + +This section explains each feature of DBMS_UTILITY. + +**FORMAT_CALL_STACK** + + - FORMAT_CALL_STACK returns the current call stack of PL/pgSQL. + - For the display format fmt, specify any of the following values. An error will occur if any other value is specified. + + +**Values that can be specified for the display format** + +|Setting value|Displayed content| +|:---:|:---| +|'o'|Standard-format call stack display (with header)| +|'s'|Standard-format call stack display (without header)| +|'p'|Comma-delimited call stack display (without header)| + + - If the display format is omitted, display format 'o' will be used. + - The data type of the return value is TEXT. + +**Example** + +---- + +~~~ +DECLARE + s_StackTrace TEXT +BEGIN + s_StackTrace := DBMS_UTILITY.FORMAT_CALL_STACK(); +~~~ + +---- + + +**Note** + +---- + +If a locale other than English is specified for the message locale, the call stack result may not be retrieved correctly. To correctly retrieve the call stack result, specify English as the message locale. + +---- + +#### 6.6.2 Usage Example +A usage example of DBMS_UTILITY is shown below. + +~~~ +CREATE FUNCTION dbms_utility1_exe() RETURNS VOID AS $$ +DECLARE + s_StackTrace TEXT; +BEGIN + s_StackTrace := DBMS_UTILITY.FORMAT_CALL_STACK(); + RAISE NOTICE '%', s_StackTrace; +END; +$$ LANGUAGE plpgsql; + +CREATE FUNCTION dbms_utility2_exe() RETURNS VOID AS $$ +BEGIN + PERFORM dbms_utility1_exe(); +END; +$$ LANGUAGE plpgsql; + +SELECT dbms_utility2_exe(); +DROP FUNCTION dbms_utility2_exe(); +DROP FUNCTION dbms_utility1_exe(); +~~~ + +**GET_TIME** + + - GET_TIME returns the current time in 100th's of a second from a point in time in the past. This function is used for determining elapsed time. + +**Example** + +---- + +~~~ +DO $$ +DECLARE + start_time integer; + end_time integer; +BEGIN + start_time := DBMS_UTILITY.GET_TIME; + PERFORM pg_sleep(10); + end_time := DBMS_UTILITY.GET_TIME; + RAISE NOTICE 'Execution time: % seconds', (end_time - start_time)/100; +END +$$; +~~~ + +---- + +**Note** + +---- + +The function is called twice, the first time at the beginning of some procedural code and the second time at end. Then the first (earlier) number is subtracted from the second (later) number to determine the time elapsed. Must be divided by 100 to report the number of seconds elapsed. + +---- + + +### 6.7 UTL_FILE + +**Overview** + +Text files can be written and read using PL/pgSQL. + +To perform these file operations, the directory for the operation target must be registered in the UTL_FILE.UTL_FILE_DIR table beforehand. Use the INSERT statement as the database administrator or a user who has INSERT privileges to register the directory. Also, if the directory is no longer necessary, delete it from the same table. Refer to "Registering and Deleting Directories" for information on the how to register and delete the directory. + +Declare the file handler explained hereafter as follows in PL/pgSQL: + +~~~ +DECLARE +f UTL_FILE.FILE_TYPE; +~~~ + +**Features** + +|Feature|Description| +|:---|:---| +|FCLOSE|Closes a file.| +|FCLOSE_ALL|Closes all files open in a session.| +|FCOPY|Copies a whole file or a contiguous portion thereof.| +|FFLUSH|Flushes the buffer.| +|FGETATTR|Retrieves the attributes of a file.| +|FOPEN|Opens a file.| +|FREMOVE|Deletes a file.| +|FRENAME|Renames a file.| +|GET_LINE|Reads a line from a text file.| +|IS_OPEN|Checks if a file is open.| +|NEW_LINE|Writes newline characters.| +|PUT|Writes a string.| +|PUT_LINE|Appends a newline character to a string and writes the string.| +|PUTF|Writes a formatted string.| + + +**Syntax** + +![UTL_FILE](gif/UTL_FILE.gif) + +#### 6.7.1 Registering and Deleting Directories + +Registering the directory + + 1 . Check if the directory is already registered (if it is, then step 2 is not necessary). + +~~~ +SELECT * FROM UTL_FILE.UTL_FILE_DIR WHERE dir='/home/pgsql'; +~~~ + + 2 . Register the directory. + +~~~ +INSERT INTO UTL_FILE.UTL_FILE_DIR VALUES('/home/pgsql'); +~~~ + +**Deleting the directory** + +~~~ +DELETE FROM UTL_FILE.UTL_FILE_DIR WHERE dir='/home/pgsql'; +~~~ + + +#### 6.7.2 Description + +This section explains each feature of UTL_FILE. + +**FCLOSE** + + - FCLOSE closes a file that is open. + - Specify an open file handle. + - The value returned is a NULL value. + + +**Example** + +---- + +~~~ +f := UTL_FILE.FCLOSE(f); +~~~ + +---- + +**FCLOSE_ALL** + + - FCLOSE_ALL closes all files open in a session. + - Files closed with FCLOSE_ALL can no longer be read or written. + + +**Example** + +---- + +~~~ +PERFORM UTL_FILE.FCLOSE_ALL(); +~~~ + +---- + + +**FCOPY** + + - FCOPY copies a whole file or a contiguous portion thereof. The whole file is copied if *startLine* and *endLine* are not specified. + - Specify the directory location of the source file. + - Specify the source file. + - Specify the directory where the destination file will be created. + - Specify the name of the destination file. + - Specify the line number at which to begin copying. Specify a value greater than 0. If not specified, 1 is used. + - Specify the line number at which to stop copying. If not specified, the last line number of the file is used. + + + +**Example** + +---- + +~~~ +PERFORM UTL_FILE.FCOPY('/home/pgsql', 'regress_pgsql.txt', '/home/pgsql', 'regress_pgsql2.txt'); +~~~ + +---- + +**FFLUSH** + + - FFLUSH forcibly writes the buffer data to a file. + - Specify an open file handle. + + +**Example** + +---- + +~~~ +PERFORM UTL_FILE.FFLUSH(f); +~~~ + +---- + + +**FGETATTR** + + - FGETATTR retrieves file attributes: file existence, file size, and information about the block size of the file. + - Specify the directory where the file exists. + - Specify the relevant file name. + - Use a SELECT statement to obtain the file attributes, which are stored in the fexists, file_length, and blocksize columns. + - The fexists column stores a boolean (TRUE/FALSE) value. If the file exists, fexists is set to TRUE. If the file does not exist, fexists is set to FALSE. The data type of fexists is BOOLEAN. + - The file_length column stores the length of the file in bytes. If the file does not exist, file_length is NULL. The data type of file_length is INTEGER. + - The blocksize column stores the block size of the file in bytes. If the file does not exist, blocksize is NULL. The data type of blocksize is INTEGER. + + + +**Example** + +---- + +~~~ +SELECT fexists, file_length, blocksize INTO file_flag, file_len, size +FROM UTL_FILE.FGETATTR('/home/pgsql', 'regress_pgsql.txt'); +~~~ + +---- + +**FOPEN** + + - FOPEN opens a file. + - Specify the directory where the file exists. + - Specify the file name. + - Specify the mode for opening the file: + +r: Read + +w: Write + +a: Add + + - Specify the maximum string length (in bytes) that can be processed with one operation. If omitted, the default is 1024. Specify a value from 1 to 32767. + - Up to 50 files per session can be open at the same time. + +**Example** + +---- + +~~~ +f := UTL_FILE.FOPEN('/home/pgsql','regress_pgsql.txt','r',1024); +~~~ + +---- + +**FREMOVE** + + - FREMOVE deletes a file. + - Specify the directory where the file exists. + - Specify the file name. + + +**Example** + +---- + +~~~ +PERFORM UTL_FILE.FREMOVE('/home/pgsql', 'regress_pgsql.txt'); +~~~ + +---- + + +**FRENAME** + + - FRENAME renames a file. + - Specify the directory location of the source file. + - Specify the source file to be renamed. + - Specify the directory where the renamed file will be created. + - Specify the new name of the file. + - Specify whether to overwrite a file if one exists with the same name and in the same location as the renamed file. If TRUE is specified, the existing file will be overwritten. If FALSE is specified, an error occurs. If omitted, FALSE is set. + + +**See** + +---- + +Refer to "The SQL Language" > "Data Types" > "Boolean Type" in the PostgreSQL Documentation for information on boolean type (TRUE/FALSE) values. + +---- + +**Example** + +---- + +~~~ +PERFORM UTL_FILE.FRENAME('/home/pgsql', 'regress_pgsql.txt', '/home/pgsql', + 'regress_pgsql2.txt', TRUE); +~~~ + +---- + +**GET_LINE** + + - GET_LINE reads a line from a file. + - Specify the file handle returned by FOPEN using r (read) mode. + - Specify the number of bytes to read from the file. If not specified, the maximum string length specified at FOPEN will be used. + - The return value is the buffer that receives the line read from the file. + - Newline characters are not loaded to the buffer. + - An empty string is returned if a blank line is loaded. + - Specify the maximum length (in bytes) of the data to be read. Specify a value from 1 to 32767. If not specified, the maximum string length specified at FOPEN is set. If no maximum string length is specified at FOPEN, 1024 is set. + - If the line length is greater than the specified number of bytes to read, the remainder of the line is read on the next call. + - A NO_DATA_FOUND exception will occur when trying to read past the last line. + + +**Example** + +---- + +~~~ +buff := UTL_FILE.GET_LINE(f); +~~~ + +---- + + +**IS_OPEN** + + - IS_OPEN checks if a file is open. + - Specify the file handle. + - The return value is a BOOLEAN type. TRUE represents an open state and FALSE represents a closed state. + +**See** + +---- + +Refer to "The SQL Language" > "Data Types" > "Boolean Type" in the PostgreSQL Documentation for information on boolean type (TRUE/FALSE) values. + +---- + +**Example** + +---- + +~~~ +IF UTL_FILE.IS_OPEN(f) THEN + PERFORM UTL_FILE.FCLOSE(f); +END IF; +~~~ + +---- + +**NEW_LINE** + + - NEW_LINE writes one or more newline characters. + - Specify an open file handle. + - Specify the number of newline characters to be written to the file. If omitted, "1" is used. + + + +**Example** + +---- + +~~~ +PERFORM UTL_FILE.NEW_LINE(f, 2); +~~~ + +---- + +**PUT** + + - PUT writes a string to a file. + - Specify the file handle that was opened with FOPEN using w (write) or a (append). + - Specify the string to be written to the file. + - The maximum length (in bytes) of the string to be written is the maximum string length specified at FOPEN. + - PUT does not append a newline character. To append a newline character, execute NEW_LINE. + +**Example** + +---- + +~~~ +PERFORM UTL_FILE.PUT(f, 'ABC'); +~~~ + +---- + +**PUT_LINE** + + - PUT_LINE appends a newline character to a string and writes the string. + - Specify the file handle that was opened with FOPEN w (write) or a (append). + - Specify whether to forcibly write to the file. If TRUE is specified, file writing is forced. If FALSE is specified, file writing is asynchronous. If omitted, FALSE will be set. + - The maximum length of the string (in bytes) is the maximum string length specified at FOPEN. + +**Example** + +---- + +~~~ +PERFORM UTL_FILE.PUT_LINE(f, 'ABC', TRUE); +~~~ + +---- + +**PUTF** + + - PUTF writes a formatted string. + - Specify the file handle that was opened with FOPEN w (write) or a (append). + - Specify the format, which is a string that includes the formatting characters \n and %s. + - The \n in the format is code for a newline character. + - Specify the same number of input values as there are %s in the format. Up to a maximum of five input values can be specified. The %s in the format are replaced with the corresponding input characters. If an input value corresponding to %s is not specified, it is replaced with an empty string. + + +**Example** + +---- + +~~~ +PERFORM UTL_FILE.PUTF(f, '[1=%s, 2=%s, 3=%s, 4=%s, 5=%s]\n', '1', '2', '3', '4', '5'); +~~~ + +---- + +#### 6.7.3 Usage Example + +The procedure when using UTL_FILE, and a usage example, are shown below. + + 1 . Preparation + +Before starting a new job that uses UTL_FILE, register the directory in the UTL_FILE.UTL_FILE_DIR table. + +Refer to "Registering and Deleting Directories" for information on how to register the directory. + + 2 . Performing a job + +Perform a job that uses UTL_FILE. The example is shown below. + + +~~~ +CREATE OR REPLACE FUNCTION gen_file(mydir TEXT, infile TEXT, outfile TEXT, copyfile TEXT) RETURNS void AS $$ +DECLARE + v1 VARCHAR(32767); + inf UTL_FILE.FILE_TYPE; + otf UTL_FILE.FILE_TYPE; +BEGIN + inf := UTL_FILE.FOPEN(mydir, infile,'r',256); + otf := UTL_FILE.FOPEN(mydir, outfile,'w'); + v1 := UTL_FILE.GET_LINE(inf,256); + PERFORM UTL_FILE.PUT_LINE(otf,v1,TRUE); + v1 := UTL_FILE.GET_LINE(inf,256); + PERFORM UTL_FILE.PUTF(otf,'%s\n',v1); + v1 := UTL_FILE.GET_LINE(inf, 256); + PERFORM UTL_FILE.PUT(otf,v1); + PERFORM UTL_FILE.NEW_LINE(otf); + PERFORM UTL_FILE.FFLUSH(otf); + + inf := UTL_FILE.FCLOSE(inf); + otf := UTL_FILE.FCLOSE(otf); + + PERFORM UTL_FILE.FCOPY(mydir, outfile, mydir, copyfile, 2, 3); + PERFORM UTL_FILE.FRENAME(mydir, outfile, mydir, 'rename.txt'); + +END; +$$ LANGUAGE plpgsql; + +SELECT gen_file('/home/pgsql', 'input.txt', 'output.txt', 'copyfile.txt'); +~~~ + + 3 . Post-processing + +If you remove a job that uses UTL_FILE, delete the directory information from the UTL_FILE.UTL_FILE_DIR table. Ensure that the directory information is not being used by another job before deleting it. + +Refer to "Registering and Deleting Directories" for information on how to delete the directory. + diff --git a/contrib/orafce/doc/orafce_documentation/Orafce_Documentation_07.md b/contrib/orafce/doc/orafce_documentation/Orafce_Documentation_07.md new file mode 100644 index 000000000..91637fd62 --- /dev/null +++ b/contrib/orafce/doc/orafce_documentation/Orafce_Documentation_07.md @@ -0,0 +1,66 @@ +Chapter 7 Transaction behavior +--- + +Most of the transaction behavior are exactly same, however the below stuff is not. + +### 7.1 Handled Statement Failure. + +``` +create table t (a int primary key, b int); +begin; +insert into t values(1,1); +insert into t values(1, 1); +commit; +``` + +Oracle : commit can succeed. t has 1 row after that. + +PostgreSQL: commit failed due to the 2nd insert failed. so t has 0 row. + + +### 7.2 DML with Subquery + +Case 1: + +``` +create table dml(a int, b int); +insert into dml values(1, 1), (2,2); + +-- session 1: +begin; +delete from dml where a in (select min(a) from dml); + +--session 2: +delete from dml where a in (select min(a) from dml); + +-- session 1: +commit; +``` + +In Oracle: 1 row deleted in sess 2. so 0 rows in the dml at last. + +In PG : 0 rows are deleted in sess 2, so 1 rows in the dml at last. + +Oracle probably detects the min(a) is changed and rollback/rerun the statement. + +The same reason can cause the below difference as well. + +``` +create table su (a int, b int); +insert into su values(1, 1); + +- session 1: +begin; +update su set b = 2 where b = 1; + +- sess 2: +select * from su where a in (select a from su where b = 1) for update; + +- sess 1: +commit; +``` + +In oracle, 0 row is selected. In PostgreSQL, 1 row (1, 2) is selected. + + +A best practice would be never use subquery in DML & SLEECT ... FOR UPDATE. Even in Oracle, the behavior is inconsistent as well. Oracle between 11.2.0.1 and 11.2.0.3 probably behavior same as Postgres, but other versions not. diff --git a/contrib/orafce/doc/orafce_documentation/gif/ADD_MONTHS.gif b/contrib/orafce/doc/orafce_documentation/gif/ADD_MONTHS.gif new file mode 100644 index 0000000000000000000000000000000000000000..005f79e12aa39dd66b5518576daafbc3d8c72d6c GIT binary patch literal 2359 zcmW-fdt8m#8pj`HXqPBr`g9cPq_!7Rt%%UZzS$$(^pWQ=5jWQJsp zI3SJ?$A}Ze8RFVLv|}R-7z4q;v~6Jluz;|Du|TlEu)whhSVUOFSR`0vSmam&EFmmm zEDNzfQdkez=*(!2t-6g#6%=SWJKgd0wN(IVImPCF(Pq7flx#!CX^7$2<1cuA|oPW zA`>DrB6Grla6~vJoDj|k*V5OTjTm4CgaOl2gVWOm~p~5W1N2tg4PW! z5^Z1FqBaTwZ~<`vbAfPyae;FYxQMujxk$LkxX8H#TtZyJTq0azT;iMpr-)O`DdCiH z%DD_&MqI{RCR}D*{wk&on$~SCa_!vT#J{Ef+oqQV9~2`!@=~tu*Bdr&+3md4>k&p) zUP&Go&)$qSMQiWhzL-||+vu55BR%uetKw`I(q&co88xTuf~%7}3(nOhPTAafzpCK; z-DK5%eRHqE%sInnF3f5;@0;dz+N+4l+5QDgtk|oR+r>HDF5|s=YyJ(3lFbIx&{_=OY6MwdEzRo9sbvl+W*_ zZ`yCi2`ldWe(CM=x-@li?tSUzqsD?!CA*EwdtN^(+MN7i#?GEMgK832+_jkcx%*XC zrN6=iFSjq<+cp5Ud+TZ$wd(?JoGGbTN zV-K=kdC!{%^?q{ZQd)xhZ_2^RhH`o1Kx_ zXLIGqbWoW+H@tOo$qUtu%c6}NNeOy<+AuX$=^Cql;{53~QH`C6lRib(?KBGOUwhKY zC^#lH(_+>jvpBu}%(*boT)3sWs&G`~nkIzh?5-SqTywE^g8>u}U-gOveWle; z9zHr*A+9%)gw;p$x|Ivc?dw-}xo&t?W^AA2Ql6fqc(8I}=68>)SIp2@nCNQW`vz=0 z{BS|^%kw5y<+%ZAX5j-0tTM8VG9Lal@aJ81dQQn13f}dOswa$d%na?-cZ$jQ{9b=^ z73lTm@BU3cB*ksTwfM+>U}E4{QR^ZMNNwFm=QjK^Fy~cCIF7B-{}R5`&c2z_myIhU z%=eeC|JO33ChP5U$F=P1MxNPm@nxHuLMkgZ`3NPA4q5glJ66l*d<0F=)HTar-hY-5 z(CMPybGiQVz}j8ZJwQLqY47y0$6r^R89b|NkIoNShtoGUIfc0%XV|L51jEY3@5jvEbZ}eTYe$8%BI}IGv2@5@rJefiv1RR{!^;Ne zu2bt28tpKNy;At^O2eW3P9Al5G1YBr^agNTndJ9=d~a`M#H1wuE|oasj^{ctB-Om| zo%_Ua+u-R=x5JJ1J0;s#PB^$H_TH7w1Cy+xuKIxE+g1H8PS;y!K`$McA74DjX13k@kL^n?evHt#>C{EUdyQFg{G;8m%-Ye_rdI6` znLP2S`f#ZJ=y_2eN|n~N{(bccV+*#HjrXLx1BM2g36ZnLKFhs3I&eqYEL%DI@8!qD zHEGQm%kCl8&-jauymMl{gfpO4js5=tedNPmKW^V8Q{?S2P!dj zncy~banjPH{M7^8M2qaOKCh=Ps@YfLRCh)Pz5LVtnpoI=d7aO9LFvkJ#Sfa$1>3vd zJz3P|X|%~X&N59G%uJdu?Xakua;o95OF`4LEtmfu$G0q1y;4}d-nBW+OyM`-Qit!( z$+1VrmuHF&QL&c|8`G4pJW>YLRj==9NFTQK>Esn<4e?W=CF*q=)~s2^HY|e zo3}A)_{n{KZolexn*F$7h|{Fb%ofKE&kb2N=T
kYN_U2@$0SW3|PIc3%^o2%!g zPadA5+|=RU{HHkCJ$lZIn(V+2)%X5$F6+Xbtf4ChPCMOe^vhJ0bIz*7;FOz|Hr9h= zy4ptFTpiGpA77h;oobrzg!L4p7-~Z1s9Wk2dI~eWHES2wv^16V6y+V!tY4#UZD|?W YQ(RiB`Dt5CYuo3Zl8Q-QDu8MK2maQ7-2eap literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/orafce_documentation/gif/BITAND.gif b/contrib/orafce/doc/orafce_documentation/gif/BITAND.gif new file mode 100644 index 0000000000000000000000000000000000000000..0b310434c96847e4f3de44ec21d52ecb5768f2ce GIT binary patch literal 1940 zcmW+#X;f9$72YDnA*dj$NS}ye2t_VY;4BwYO93N30UNUPMiVtf(ri#uWTj4I@XnV6b?dfT%d7pja`An%3Q`=f_!RpS{-kzWsgQnKCso zGHTxWeiI=Z!2hXhKY(Zm0du{$`|8C31ONgMfCOMr02BcVfKmw%M z6AB1L2nB^gLXkpYp@L9_P*JEPR4G&z8VF4Y4TVNRlR{&mgV2T0QRpOeDRdSF2tx=1 zg+aoQ!eGH5SO`YJBv=YId6;7h1VkVNY0^RkP!XsgDujweg;51m1*(WDp(;^j)BrVs z8lpz1Nz@p1KwY4Ys1xcEbw&fw5NIG8goZ?ekpWpCBQhaNWK#lDaY29tL?BH?NC8p= zDM$*DB1vIVfmA^%l1ijXQkgU$O^}A95owY%CLKr@q$BA>x+I;+05Sv_NCuH1$zWn2 z7Q{$Q#FE%l-!xlDKnWy4nr27^q#~q(QX#2GsjyT*szRzLRg$WdDoYKdCZvW^BdJNL zvD888Lh2}WlDd>SO9P}Kq=C{PX-H|XWRNT*qhyjSCG(3Qm~NPon7quU83hGc5v(99 z#EN8vSp`-FtH>&`Dp_ULfHlDyvPP^))|ho*U9gU<6YG+7W&_v|Y#D$F zZBue{?w8;%Q~z=s$j~>)*IHG2B6pxy@SHPMJ5GJ&@oLf*YqkCKIv=s1{Y-UPL&2Cy zYkjBgFK;Xgny$}j++WeODfE5kmZ>#6n@b}9-qqe%v+Kw0ak+y>B^}s(uFP5#JlA>P zo7P>aWl39;YIUP>R?UJAXKm%heQBrGj!Hhb=W>JLl$#ss7ZS(IF|C0~6yyBNqUv>S|YA@xzl*qhE{cW!fTiWii|7O!>&*_*MRnyr#DCQHtX%@)L zwI@eB9-OwIJ8xgi5AQ{`;k{LL;`aFZks0?tJFd7bo96qt;kwI)x*3i?bpGdd*osLj zB9G30R4NN%j(i^eZNr_oiiJ7%o&Vdl>~h>}e*_Q5zgMH(k z$0Vh@wagg(=F|BzO0~WnQ)VxJ@O0+J@Ce8ola>3y#`f3<5AU>(obImLmwtgW=UE2~ zEgod?`bSWz`zXJ~lj4J-tyh;13H*L+!i1hOTUx-QWi9hPhFmK2ffN^RUp3%fWJ|vD zyjRIaSI^u;TTJX_OL3!T|BY*%K-X3d_w4_!dsJ{_neFkkfSviUc=MV~ca6-{!1UCB z@`OzAxUoSztuOF##);W~3%NkIa9{J=ZxXJ|@vywR^GQW~aYtNA+81t#@1!N#$L;z& z<4RCr#pb~aEL;88-rN=6-jUN%Ve1_?ebKC?hYE_4&$?aT?j5`)ao@}f7XsJkTMn$u zE*f6EW<+?G~S|_@orU4O5u)eU9V)-y8b4mDsH#M{0{~0!vgk} zkMbNewzA86)oc4|&;HZDAa#7@&4%x4FJ+!yGxr_hpM~! zYCp71^l7V_vFT_<`rx@Kj@I1YUCTNhaxQvzxc}@U)qCG|OZI>cN6O+$K7U#0c>iJW z^o%C&5wQn4FL-x{-6)Rg3Hcx_=#T%>L$iEuCO-OpOjyHb$0F=|zUvs(J$UBTdm-@H zv2DVo&~>rTn%u_ab~>WNhsIIF?hhk-l7C%z=+N;ynceyMo;!bSjviV0-^3WF?b)7( zuOGi%-ge}Z?H@l0J#6cnR$B6N)RNKD>>0i7$rYWaZa$BRz2#l9qJGhD+vfbq`bM~= z)82aY;lF>>CeYP>*Dn_RGA|&bINZne_3#t<_X<`%UN`!=Uw!=Zx!GU%<+Sb$$=vFm zUij(6x2`>k-JIYWm=(~|f5r?aO%p>F)g=R(__cBY*SN(|drZTn?hnWe$%Kk?bE z_1U&E-#gZZ=qnHMm&-E0zB3K6&mI(L?&V_!Og)oe=`CFCky1W(MB|yn=-v&5wsNbB H3&i{%Tt2du literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/orafce_documentation/gif/BTRIM.gif b/contrib/orafce/doc/orafce_documentation/gif/BTRIM.gif new file mode 100644 index 0000000000000000000000000000000000000000..afa3aa4fb5a9bc6cf22cf6e72936cd23ed21b0cd GIT binary patch literal 2448 zcmW+#eO!*$AO4bBnN(V6<)xaaJCS3ROiM^FRw;KPncj}1^}*tiS?-bbg5DmcLyCG- z?o|&(B4LU2bW;6vqxCu(nuIBBH9Nn@&L8J;uzuq@d)u4@dWV{@eB!o1cU^P1cC&L1cpRFB0?fYB0(ZWB0~}&2_Xq1i6Dt0 zi6I7vA;d6Z1Tl)3numI9gaKn97^s>S<^XdDa~N|3a};w7^MHATd5n32d5U?41;7Hr z0>%Qt0>uKuB480=5o3{Hkz$cy39y8)gt0`hM6tv$1I!R+7&C$y#Y`=MT5&`G6Cebr zS`oql;Sk|4;RxX<;TYk8@QCo3@PzP`@QesR1VjW(1VRK#1V%(4A|fItA|WCrA|nzI z2@wesi4chri4g{bA;K_WgfL2&T79+IhytcSC{VQ-lmp5k%3;b8%2CQO$^+#QRflBjv9o9ZZVz&OM>%s9e0$~eY&U_4?xW;|g$Wjtd7Faa?EGl4LHGJ!D>n24B& znMjyOnaG#~OhQb;Od?F8O!@$(4w~9+wdCr#{{#Oo_5W?!PhnWc)cySFUk+#+T5q^} z{>-gJeGAWQ_X~Nq4^2f;9d|F}*QL!|mSXBrrl^{-O+u}iR^3VVPvzSujniyzn7qHTvrSs&K)rt&9eQ=L+M@fq@8P9(s!y#^ z^v0PZsV{?4KJw{)_0NI(2Bn2r92q}8b1S#+a?Y5A<52A_XQ#@?Wm%*} z))a&#erz7QY)|CTIP=t?tFt#}jn=x*r8@@YJXJ zM25Rul$I(3IIi_#OQE%QRDZzKN%@D%sO^jX0(&Q~RppI4B7F}cQG0#S*fwkZtR3SQ zddNGTo-0bRo8zx=IW$o*yFw$ReLx@)nQfJe{J59bM_j+_zp?oD;jJehj@_+r8G@|& z3r9cOkd|+P4EebrJ2srGG!omfgG3&5kCfo_Maxh;i}~l-VPF z#oZ4^x|Ma#C;N+AVn%OXuF4r5z;CZRy*kX|i(lW=^ZrLsH|0y&pIa6*O%BOfId4h$ zZR_xxgGqdD^Ux#Nt0hA*8Z)L1c5Qui?Corwk4i?qPEPzcwWXxHb4`D4vSYx&lo|UJ zA=O{|z31iLqczW}>fY$t{A-~D+hz8py*_)jF6IVKa?I4s>yKX>w`q)p`HeLFTR|>o zdiv)_I*iFl6RO&C$2s_!)o8CsFuQQtEj-Iew_yJMF}+Di+s55WY{pB@NAd53-C0SC zQlgZexjF_l==SI7W^dM|=zd%1I(zkz1151s3a2{`2h+76|8brL(F#@C{V#f^gZ^~g zPEDtI&b8~Kmzf!D->q-ca>4xlm0xZ>Vy!E*3=(+LvIp^69>_fTm%TIB<%`+ci+ew@ zG_=iIXE`(BuJgNr9|PC8&+3fKm^_k_X4cz#Io9FMsR%95yDv}2@9GVlGmgqk^;}aQ z%rd>OAaz#4CQHNpg~8t8iI>WIFO^N1*P_3Dx!Z=OtIhd$W=-zh^v%(;^65r-gA1*# z{I3NYk=+Y6yDncIRO_?y?r^oaC-QN$o;BrIeVy$o_rv3Bv*oHG$2d#H+701G_6_w- z@gr|@LANM$a_Gw6qbDXj8?4v!IPl|YO}~Y9o&}qW7V0=&Ez8iXQq1{yfllTTF2q>= zcts7Viv7vkO#a+b$IvfL`_i8Q^E!&g)FgSn_04&H6t8c}Igz|{&&ZDad4><`=ESa0 z49?$G`Nq#?#)9%-=yyPgtv89yy`fBYmSSpRG+!%`MeQamN72{y5v8w`WcTo8n~O>5ed^)`E?vh-W!o|(>n7>nN zW9gDylV$NchO2sXmAmV_y8g5}F?{X$SIRvP8@k?I9j<uvUAA8BK)&Kwi literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/orafce_documentation/gif/COSH.gif b/contrib/orafce/doc/orafce_documentation/gif/COSH.gif new file mode 100644 index 0000000000000000000000000000000000000000..cf3a31b4967fa6591bce7122c61d40e640fe5cf7 GIT binary patch literal 1855 zcmW+%YjloR7T!daNGfsZv~efn_F~AkNOY>7g!q!U^Ug4X6646kw3X2rtggyiR6Mm*Rc{xAMI{WOk&hzZ&dDc2(<6~%2v$91wpd3_=QVuDHl_N@n z(x|jjnv`axMHx^Al~Kx&GOUa!0VSwJDIq1S#FfBR90?Qw5wNQWSwI$&rN|<(m@FY1 z$VRdi*+e#zE#v?>NRA?h$YFAXG>}HpiZqdC(n1EvAQ?r5$S@fp0TLupBt*g_uKKRo z$e-Uupo296w1@#QC`O4PF)Zd8m>V?LZC7%4?&sjoQ~!KxCGebA-8LwdS<*MSHC)Rh5S=%{o|E`l8`%cv}Pwvk<_37Yw z)!A_+pPk+kx%k%QlO_2z+dV4-yTl*ZeRijJWB*yz2mI&qC+&>i8egi-p6MmYb=9SN z&KJ(9T-zn#;NFWRN$2#eQwIxb4=uV@y*1&`zPgH~_iojlI#gJH#0hBAbzE7|)yniI z$KlJe;%g^-y%M&KE8lGhI|aqkS4_D#MY%eTD~mD>2=LASF|7Ao^n3mz17xbpU+vnMu0BtrMq z5lugz-;h#0wB?rfGUrd3HLv*?uW$AE;0xb`H&EK_4_cbVl__DNmEFS3gVC4659SoF z@DOKpNe^xpvoq;%RMMQc+FX2`ot|9WXJ+rj%oejhg6Y$J>pO+M2rC2X=FDAxW~kTK z?AW5dlU|<`=naofjEoBR1=e|j=1sd4`^TUTx+-f*Z14PCV+J4hpkgS^nPN zZzM*q4ExV5Pg3NoJ+?G)DroY;eSiMlyP3ld4-bLU!`Apuw-}f_sAf)6=ZC@b68yVr5+83lSpBrI z)?3@ZeVU6lof4;Ef~C~ z-P;$kD0tZM?pbBa%MZof2we4;+PA*(RzdEvhyK!6TP_^BHRo>Yn=y_4oA)PdkLh^0 z?)%uaFCY2gmo`PC7Y_PMpNZvflvb4FeKIofAFWKxUvG!Z|LVyc-;r`Rc+ z+%U z%|BPo%-PJhH2;Bw`ax{WZ8yx^s}HU+a$+(iDiGO++i$)Pk9Y5dcg{WM%&#n;@xt=m zn1VIHllMpG0EALxXQbCW^2i!sKmY{{4sZm=Z~~`r1`l|GXLx~EctZdJAuxgQ79@!qXbGsi75#srDS9vBQhovG9}A+WNQA45=Z7J9F?PS0w?0coP?8dGB&Ug8?ylWy35A4(7-ln-gB2Ud@{YSRe~*K`f{Rvj~f1ku8cvwP==Li7c@tv80wpz_OraZfBBbdp`hw zkopH#uHjzZgo@(_E;QwiEvdV7{L72Y1;thSDoz~iYAK>O2QHmx@BZ|OvX3WJo;=jk zI(c!d>pA&V->xYudiPbH>iA*rjCXDi^qe|;`HRsjP*ipL>nrUQttItw+QSQ55Xv`J zt&P>4ONtu?zk9y(;<1?**4Ixie7&~i`!W45u4+9xw{iNGV>@~mjR}>*10AKEy0T+4 z+VajV@1K3Xee-j-SI@}9o)259_jl!A|8{m`!=$mN^Pl~v>~b#U6g;&0-+?X5KF=T5 zB-fW;oj7Owma|Vy9g+=ae*W#S9x3V@p84p8iDwViUk}yA1MADybabC@UUp~IwDzHm zEAN(^o78q>-J!hUQF{wZ+ivl-fis&Psjj&{=bPS|zYD40lg2-GU2WfARJ`@hy}OT( zD|&5H&Ct9Yx{R|&w`3y=5>Fx?b^2S zSGK+yW=*a5vFWj!T@6boZoJj7_N8A}Zm*}ChuY^@*MBQ2%YNGZ=h9b>9=`F}%X^w{ fv@R@vX0UBZ%^QO|mhzFoopta1HMlb;2ebYMMw&(v literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/orafce_documentation/gif/DBMS_ALERT.gif b/contrib/orafce/doc/orafce_documentation/gif/DBMS_ALERT.gif new file mode 100644 index 0000000000000000000000000000000000000000..3da9af7c9a87489a144cb7d279d2951b1f35dabc GIT binary patch literal 7138 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 I#SYy0Kf*T{U;qFB literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/orafce_documentation/gif/DBMS_ALERT_flow.gif b/contrib/orafce/doc/orafce_documentation/gif/DBMS_ALERT_flow.gif new file mode 100644 index 0000000000000000000000000000000000000000..60fa9158c5a475eb6f329cd350efaa3be48568cc GIT binary patch literal 4959 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 F{vVN9a3uf$ literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/orafce_documentation/gif/DBMS_OUTPUT.gif b/contrib/orafce/doc/orafce_documentation/gif/DBMS_OUTPUT.gif new file mode 100644 index 0000000000000000000000000000000000000000..1f4380e56b0f2fe2b0eb6c0650f057479b55a8aa GIT binary patch literal 7495 zcmXYWWmr|u7w(o$C8S&F0}5;q0Vz=={ULaiR%+9zph$a2Nu@cP5@`)7#P4H00srH0DuJnECgU-0E+-v6uI!XgkBg>V3b10fs);a~`dKsXd401yF$2oOYoAp!ysP>2XXL=YlE5D|um z2t-676o60=LLmr+AryhoGYHNQ2Vnq$0T>35Gelqv0AoNH1Hl*=#vm{Tg|PsP1z{`% zV__JJz*rQ<0Wc1PaS)7yVH^VEP?!L~1P~@bFad@M2uwg>A^;OXmbU=)T?1V+!$KO-AN00;qK1VGNnKrjG;0TB#@U|ZU5iEjW zQ3MAdI1s@>2o6SY2!cZq0)P-ega9D~7$G1C0Y!)aLIe>agb-nbh#*81K>-8>5fnmD z7(o#PMgJ#+Gd9j3Ig9JecqRo5fMP%t1EClg#ULmKMX>;i1yL-7Vqp}EpjZ^e0Vobc zaS)1wQ5=HeP?P|m1P~=aC;>(Z2ueUvB7hP>ln9|j7$qVo@qff3-a#)h}hyLxr_s?rly`o1?vO2x0vH&hUCfPD+i6V3Ha9qpNZv$VaxsNylb%A*bg9196N zsl5|H8qg6Q!hB)KaZ&Cz;KgswOFe$vB{yyS@&sRRV`%IoCi3u^K&~G9dY}BFaRQ)V z<#CZh^TD%D15r&Admna@yU+6ER&(x`))iVM+@)F1Q`9~-%foy66XMLOgjk_$)y77 zW<0m!uQ+elezuOuBeBZqdnhHQepI z(?Cfq{$ao0pPJCZOiuTx5%qz7-lgsQp;XrK*Jr@BJ!eTts;f)WCH5n)oJ0~7882t~ zCY`bKyNW=@s}6p{Q0VQ@Cz>7+J+r9V;M)Ba-2Lz9oNN6?(U8&+u~^SG6v zp0i&v;qCqm zgxuj>T6!z8aP8(Z}JT-M9TlPmK;o_acFKj249?R@piHghj(rBJ+QXFkrd0gtoJqJ3y z*sf1WbkWP*>`S^3P(vHD^yl;#6Im)t8d+^i;<*0)p8A8>0`;R(3HisI~P0t)oqQ|Coli$Ka3; z&4(`j__)v=lFJ$Yy}8~cLx_vgxcJ36`c99XosO4Vy3NmJhprpPj55};D}0m@cs0wo z*l_M>fg^mc=8RpO-t$~RzUfYFQ<>BEA-!*~?(P z_?5Fdywh){I{6P`C3yS#fBsk8B6e!>Ri*oeuU(mV|LuOE+IyoBjk}4Nm2_+D92__9O^g1~_-A1Xege~hR|F}NIb-CV74vn9 zrpA1DZNpZP!dvoAW9`vv+FHy-?mM?sIfY1Hj)g_Sl_{&r9ZkClN|F5=rd!s61W{YbTRUC(I0?hzy_}x_5LmZ!P zFGPP(sEQNPI(+)NDaFy~q4MQ|y)TzKSkC{h@6|7Jk9la4(|(ZOK&uwi*pW?pyF$vv z6n=o^d{B8<{EE4`(DGaFLq6|`61!=}SgLll>N)*N9rq{NI%y2e-w&E-*2}!iX6GhU zDuuI6Bm(YX1pZCPW!v(IYP{r_{#xm2BlPR5*Gb_Qf0+;OP9&W%7MqKE`9@{zJW^pE zeO8PQ@!9-`QX!0yUs_Bj+Er|&jj*%R)_bk&U2IT^oul6Jzf|@f>3{aGLy<)?Bj*M! z72cemRL#oARh^jp{6r(CuVQ9!k5Y40@}2Z=lDy=`yWr(i`A-W#z-z*vm0mXru)!%eWj`9^<;T+ zO&W`)Eq%|?)%yHw@o#=#e4i0m= zGnL1Swj2y&nVnA9lj#`)Q`e@KgK=A8jB}NwbyvyfRvi9wO3YQ~<@@-3H1_(-BSR=We^04XLWs)^@23 zAFqw=_M{V7|-Qc+T7xrt;6o_Al`$yo*TL|J?~W#*{DiWG`%9P_D#0F z-DcnxTE7+^0GdC+&&~2X`tJ-sPy5IPh~6v<9>cf8vw_dgZ$by>4HSA_c=aTd4YMk# zkNn(=yIiObe7X=SQ<=#kXn%7POTo#&pEZ;_C@zyq)rWqU7UM5mi3&H@N{e0gq`j_sjORR;m}J)scke0o>w z9q6Rz`F!%V%##Ao7P;NHuvgn*-6pTW3*2U>K?Q=#HZw<7;4@(dDMjhn8(Nn+;y=9(aPVlUds&QjEBFB$%|iCpsvh=A(LVLkGlYXUC|J#D0e zMbyK#?7Z^}4eKejzJ&R=O6xi@L~^ZKJV*$Ua(LBxjYIym;(6BSph+hBuTFa@4{u>3 z*Tch{*4#Gg-aHR?`xE}gvGC1F@0%arpM`a+!UZu$qPoAJ*i++J)WP|jNgSDXT&tEC zrC98)mNqRM=Q#P2T7h}H&_-i5ZoW5;eLZ$?CFn{YW@RsEeJ}pX5$-MpOGUEdgX9E7 zF}oYn3FE&K)YkQGDkQ#{e5o0sa~n=fTnbCnqt?+qN~ErhG1NA>Z<2I-GS>LW_HIK` z!o?&@IEj=a*{CSV)-eh4PPRUJ<`j`^Go8#_7yq=u%0eL}fGnAI)KNCV{IdvCiKk@V zVT!LKYm^Xv;O5(A&!q4_j}Tk8C^4xS5!b)?iFa^(B29i(1tf>tC08z+zcGotm!h$-Ar~&EWOT zA^pp_G_y;LOA&@-k?*uc*iGcK(W&C1h${`Gp+qL>DTKU0#1sgcrKM=>NI?`=Q| zgF<))T_A;Syy{Me@|;+hcmsRxk^obG7LyfTW<8AZUWh2DNGqy#)g*`M4{O{(h(m?E zu9u$MvkYkv@8u*ZFGW#>&(`9!Zprg}GWA*~HB&{0;aa~bOcmLA0KA%U%ofv$$^X!h7T9>fuRkhhof(K^n7o`vt)BOOReoUiO&gpYhR++JU`!T&#(Z3m?wGb5 zma}C7^Q6KZt66UXgzNMujW7A=#8`P;RMVfPc9_XkR5BM{ zx^R@m%2;AwC9ADt&g&EKSDLC_#;a9| zr4_kq%w%hBrQOo>t&yUw(V@j?&(@sm*BCe}>M7OcUaEb7sllez;xEgXv)zoVmg73``+~^y>F3Iru-Coe@rU9nBmhOI1VGq=blyh zKI_j+cBxOpXRdl3hxfqa=CR*?6@ekO`+^qa*ssnF6oKB`)Y-LQ_Ci4>olom|u11$l z3n^1}ng084f16YVnq(NU?ry$~jbw<3gcJx|&22do16jAHKC$?|nq)H5pNis`X%D_5 zWY8NhrIlbnsN+)#q*2dnw#p+-3vvy-K~7qC%O$!b4L%>vEswgJIr=c@(_M2ER~|Ic z55$D>9p|@+6kPw?Ea}reHBfrTB|T7_B3Lo}djCgY?%wf8bAIusrRQB#m4yi-g|2&r zLvUXFOx<}TnfOqRYP3i#rIkF_Sg-z7rh{7jNp423a~CmSxE*ZWjM;Jf0aFBlrngTyJdvybhMCZ z?2^9(uf-Un{+Or*CsWH9kHz2>Wf9AhAuDHQXaExk*R=`OwwYB-X_XBXYu}*%u%tII zHix9iPDGeBKJ_g%Ko$C<#rhI`2ioVVc<0BDj$4<_2e6XbIxP)7<`wjoc zSrPp1SIzY0-k-;c6%GkO;JPr0+^Bqj`8gW1yAIP_R&msI+{c+`FK2sdu?+nqgOn6pvu z>ta$JE2SH^n4j>UpUjy5+%i9-JfC5eGe0;l<1uyWuJC=(d(D4gBV%E!WnpK2;pgeX z9?#-0e+6szMGiD&@lVU*@%-Z7)5U*0UjUUaB$i*u0?uZKKMy~*^~~65aUaAf6CyK9 zlsmW@*(frXs9Kk(13D(MmyRSs<{`-gi{*X)W#PQH|_fExNikuB;KG{J)*wn7VG1eYV=3p%T;hsyrmwh&hXX+CIDq03{O1ZK6L!B%A4v^`EhAUA4G7^|5(2=-*nr@0yYF`u8njvSn%9 z&VbG7S1HNTwPs{h(*kKsoyaIFznw7%|C=yqop{$;`d;PxL;dm2j-kEu4u;+&5uR~+ z|FN-8JL2NXTLbAW-EPBOnOpnCgGfN_z`rfu=&iBLv5%J9HSXJ=?{3YsZWjn_EvUeL zJUetGJ73RtuLSH&SnX^q3{EWUaL#Z44A>gI@#7os_8~9PF@&)#XtT{@Re1DA6X`t3 zJ4v8?qw@UZu*9Ug#X9wWY*F(Q<(aE!ab_Rw)%Dt)wF~7x8c8Qy^(I+f4dQuM3m4WZ zR5rX+SQy`}bG+JHKc62N@8e#Y@~hWuOXSj%D>4xh#D>|=!nIv{#k-=a@8Mp-QIo#e zgtmqFPNhffXb((GAr}a^qbzLP1X%+IR+yU|z5FWp^F zzI6~UVr-=6ZQm?CxGqqQ9^80mVsPR6eAyGC#;XC7q$lB9f5bd2$WwS<`+v7Y53^lo za`g`*ZXJ{?49triGK!pD5QPGj|9w#lBB$RWeQIEJGnAg^yzL)@)S$ucMbaw3xGNfPk=Xxiu z8RqN%C;mc_(MbcqmRfeDVmBX@S+oR@Z_0@iE9^>-Wt}~@OzT{Y)*D% zhjgr)R}!z%>Se6Ch3b?dxfM5QTY{w8X-gZ`%Ybuq>mALv5_=W$w9@It znU!x`m0K`)?r{74KaKu^Tw~PMMV9?2{zo^5>mT?~TEjj8vykmx z-VBL2%@B$B-9B!OME5d@VlpOOf!o1Mmj$2QV$NeOb#c!Fqf8O4*Wr~kH|?q}g?%a} zF)qwfvg;4Y3fbw5WQT{u-TT92;7&cRXF8V+g4zwbYp-4CuaJ^l`C9c}mAvs>jK7Lp z_(ZTC4-4Q=#j`eq#geFv>B+jbX|~Wi2|}uD`@Ab}G5&d@AIe@?m;}{%UbYB|)kw+cuUpb3mrX$*k*& zvcsJ9tINr=Mp&l@re`e}*Owz9-BiDZaZh>=OwG6yKiw{JBj=Cymtn+mc*-q^3~LV4 zUtD=3=_bDtd6iXe#@~(VIb8xz#}vOS509trYZfQGLGiB^neL!?Z7seq){8!TS--i% z?>=YtXSa`dsVvGIpV#_f^o`IoC4Kz(C!crPhO9~ava$_cP8^3OZKl=|t>aGkQ>^G` zf4c!`im)GbN>7@9Pd2Il$1$b=n>g|Cg&#h9mzlMUjY)t-O1@37Hxxdm_; zh2fXq%By&PO?em~oJ@_L%5eeA^CMLy)IYkh#PdX;OTsymn28}Cz>ho zPHH{uxhR%fybp_Iy*C{%cvJUi5(OA#T=+&RjA-r{Gh8ei39Z@ue7GhUEBztH=%Z2Z z#&;-BC6M9Xa4$_2L_$L@=f-!NG^?%RQc^(RD5I@D{}mnpR~sCl)FkhdXT zH-M+HDZCPxrT+4Jn^kLmug1F;DziL8KC1EXcX&oYW{X(?uJStxb0%F8p56&rG!7kZ zjv{&Ey0E$;wC;&hkzzq1W{%0@z^YfFPo&CSn`}@0whQ`Fi zgs!gc#l^+Y&=4ml=i}p}udlC?l9HmL;?mNRq@?84)fFQnqnn#sU|`_x?k)xfhKh#~uV23e1qHddxW>oFqobokLqpBX%%Y;AtgNgC1_n$_ zObQDNkB^U8SXjiw#0(7$@9yqOOG`aHJ*lawWo2buTwL1P+UV)&=jP^4Pfu-ZY|_%w zA|fKZyu5z@{#{pBM@~+@x3|~V*9Qv=%g@h0Ha4cCqXPs2|EC-OpX$p^5FjKJ6c!!< zhD1h1$Hd0PCnP2%r=+H(XJlq&=j7()7Zes1mz0*3S5#J2*VNY4H#9aix3spkcXW1j z_w@Gl4-5_skBp9uPxNp=&&Xy>E?Dd8&<_-v)dkTC>o3V z;8d#wCMJ(3(yLb*Of=GzCnd^u67x5e&SdfbC$xdFSp(^1-U`XY1VCbOTzU z(TOuS4~7UR+x*eDOiyRpe_ZayT(nZOglRm7eGu{n5jBJ^lm5|bL9g{k&^S@*>3qB_ z^TUHy26F~cE{1*Gue3k_tj(F>u|m%XeY;*ioXU$M`bT~fqJt!{UCa_RIK+UV59Rk) z8(Nz5uoRVX#((<;k-RW}Nq%}za^!t3PxG%pDL z;xsoGXLBOUK-%`SFuL)8rce+9Ypdv*$YWiUZpAoRltM~lTb7}CQBjzgYG+selOU3o zs!(FlPBkr;@v>UJ#0Q+_#1od1zi>5qS=SHGNz;+`aPWaNaRF`Ej(XqtbEg!PF`v0l|L$O^|6wH zXupj-M)<^2Z>+?hG+?ZUfvyi~8K|NN;?IJH;b_PFRA+8Bz}@rIMlPWs&hVCRPfd@I zLY4XDd2&8Rs4q9Bwd*=n1HZ8<=0kJzgHNNT7}IeCpu17UpgTFY2`}~-70*Ztzj53nBGj<$9wDl^ z5TZj(>Rx$%RZDKeHHH&eNzrEPaEPGhYiD3ND`-&}Q^pFXfW1%F4Oh4o~yTE-pkk_#?9fJRl z_u)+t?e9NUDgGId6U<~7tp)oe{+!pCt!~2RY#ZTc@+rJg6N$N)MSpAk7eTc6*a%Ks z5+EbpH)HRZNd93`@%n4WA^8GC?eRg}&Uu1|gFNJ-2$mD!3N0RN*vRmqPI3|kxh_;}k8v@Q!TJD*xT<-3V&?=uhTNIy;=R=w@UQD#6rck$+ie}Sw_-4A z5xR&l;=YnKM4se&v9M@09anJkj@Gdokzm7X%;`~8-yd452m6n7Bz9t4V0LaM^y{E> zt4sWDq7eoDxvY%Dy;FejGDTR(sJSjEF{Ja8x@p!7caflmlx~9RAA3ioWUgDBd39Ed zJcFE6BUMTkGcJ?unLI#eIJqdhgqcTIM&sg6Yv^8+CT(K+^Kmh4g!+VCdvO8`z{(so zIObaWGo>f3mY#ZVb$%N><+xapm0MoQBPXDgDcMAF%uSay&oJW>@`IV6A+O3kMJZr1 zDeE^i>|4-1Zj?ni0esY`HX#g3uqiqr2fxlYw1Ig#werFlS!8K#t?vywpwMJQ8!F;4 zpgV?{o3-%C*AdC@-?%x7uc=`po`%wc3u+Lyx_)^&vDambvIa%o+CI54YQW%VX4go9 zu1pXW6ak7Rf;ldxpw?&8HNp?%KT-f144EP*5?ptkUbvkjtAp==HcvR^1ml zF<`dJWD$b#da!SWyfK1cYR+0{_>qfc823kGZPGnrz!e?EmC@CDz(I=DB5=PEMWoMC zk!UP&EL@NZi!ZQ1IiVctgIOhEQsG;Abp8(|Qf)6r>JgV?9{K&LRFf0lWqJx{RY^UI zWUF@M@m72O=}NPzVV#W8`It{<9#DiL(!vKfALn47>~jnAXMD;eydEe5j=|5yr4{hGl{8dE&s%zB-+QEtk>Nzm0ssLIPsmxb5!NFT&b z0mL^j-@D5a%u!|xe1pHaPQtxXt|1-lVWy=5B9&1jvG|lSViJUHeG!Kat6zX`A~)`2 z<|OD!*UIyAGy97)2V6=al&GB_NEI`*-sY7t%&m<{vD%~{>OikCGW7hk-~6rlk`lgO z5vRxGU`8@aV65weArfAE$&$dSZ;zQ1`1f2<>8PZb^$&}ZA?EK*D~xq%CyF+AU>4la z*2)PUCA_?&VdaqMS*sKGswJDzSjR|c&~4gYf|E*8>&|2lpAbsTwPI@LUSnrz*&4;I zuf%H2i-=4&hEYSHb=I$K_=#a0x0x7)l!H<={{Ex|msBs|XjzfhQZZ7uC^{>`!!D0@ zP!fUbkMitexSy~7rlROP?*@d9Z{GDTwH4SBAN!KuI~Pyge&n$IJy-dZTZ$untkfsb za_Q|{Z8J)$u+F|TLG!JRXMJkS+L71&+O@g%53O!2``RVWS9IIRrDN;w&2fAFd(YkM zb1%`i-{tpR`(K>nnvD+#CBA<|L0@62z(?LKW8oelIRET7FFe}2dh3%?_Ab=ljdz~= zP?Vf~oo)JfuzV9-b(HWa%j(@Lk2~I#i5Q#d`uDgJ+tVff-FI~>_o6+b^9C>AHNu|s zMTsWhHbwL!p-)(~SEhI1viGBy@AF`!%w}1wM-{ha7Ubxf=xP*7G&P&+Lw z0XmVkO{k1znBy;NIG5ljJ{Nr{kQBdB7yzVh2`b|Y>B6&!PcS@q4%texv&IRXeMF8< zFh_63d6SM9`)1z}Zn$0IvqNj?XcNwv5=dSiZhwZFPG>@d35#8>&WmLN1cqV&A>#9} zeET68=@2yOaC!RhFmmBSFt~gj-~8_wxIRK2Oh+#X&q)v;B6~vNcL?DFLukf90bx;A zB~fl(k!Gb{!(fi566~FzKwOFF>GkN@m*{!?n8kD}hjKrJbQ8isUBH304o_J4f=TKS zWg5TUZaZku3+x_je#);*?4{P95?;L?89VN1Izn-}#{CyZKcL*-qup@L(xR7Nuiq>*syG>ZS;Ir#SJ(m_cN?*RU`>ZaN8rCHjf`FN+CP`1xm~N6Wc} z+nUA@WKi$pLFJ zGBGnJej@4PVC4E%Q0he1tJD|Q^-~%-z(mtRvbbw#RD${Rv^ zjASb}RCplKAI*0hLat$f-b$3$Wi8{v;vea!68%9bA zKfa`39*CUyW|MN5O4m0WX~IW%46brIvTV4bEKV=vqKs5-O-C8IP_MMU>Wbe=|7Pqls=F_(cL;q}e%^A?mD)adk$x9(EP znvWe{!m=m_C-%6Kdb6FF(Jbs$3&7kZ087hxLWtHVU=_*U@DLvj`-j3ffjsJo(2!SM zHnd72z)yB|H81dVtAdzuK#U;w|H055xhc-lSr zb8CP+yXQC2AhjO>k-;E6vEqB?ATzTm*@rtl5q%*ykfXYViy6rCLD#oA*zGbHhn_X- zHNha4INPMJb#faGP#n|$v>lVo7-Kyc zc6I2ULL6_n9U-*MU&9Y;2BP2`m{KkJlShrhBRj=c_RCl-6P`up5ol(H(bu&k!(MSMm5f;PeG`zv3;R_QravY zHKA^XfiN-c8Exu4f(PWB2`rZ)Sdx4z;PP7_3ti#pqLC>fw%bFRrsx`FWU&7Ntx>~F zWc)tcHl;?uR2E7^Sae5NZaOFM;;#=)s&1~Y{Ebqvh+BVXPUC$_L0IV6VKz8=7O_LM zK~=+!CIE>ifR}a{xHCN}J2wR}vd1DKbtD(0CJE=_?|&qWY$A*Pq*`2XoucoUP??^G zqL}{hY8`l)gXQH}Zkc_n=!c+NsS?k$asf#fg(Q1$uEhm}HH0dQxZ56<=~I`JE;=HC zGn_jWs3@JA>y*UUP4(M6xHiq=hE!M!WW#wO!6fwSdS#bHmI*7IPwkfRJDg@hoKJ}2KnqH8 zIJMDl_;pl?rswP6+iA^&^-}-M@|;b{tQEozlS(XVO#d3kP=_X^w#$kI^~=Q~64;?| zLJ{a$($?nP1$^;?w$ulQO8o6MmSv;Vai1N5>g%4c(@{ALp_K^o^3b52kgg=JoVB^X zt}#-`Jxe0rGaYskeRg}}t8K5h_YB*9^=u!as5oC0G$QR?=OC76FP!fTKPcrb8csgP z>~ZgmEh~*mz2yZ~??IvN{O+0GGTg78+U8B$=jhqD+}yu%Ohk|EL11ykzS+UuUD9Gc z_(|-3>f2mqxOqi%fD-dd#I%JuVC9wMklSf=!SIm(pQlImp)l#R1mEtV80lUF@sXtH z!B?XrS))#w+#|)<71rG&6(eT_(qoO%bVodx>0 z6CRuA_V2W``|Nt-OuhDOcli`mIUVwEJKhOsb~wS+It3kj7F#=>r3@7Ni%@2C5Gr~R zWN;zzgdY;D&T%{AzRJ40zzP?_nL;&dPI>-EpY*V{cD{T77p8_sk&8%g3AfIahwOT! zxA#Ph^omBBpTQrW$7WR0S#i=)c#6 zqUV`L=W6hxGln8Rckry3Bz`sR+I3&t?~XLBGM#gaMUYE5dudY_>ZbgyxuU4S{6jMK z#=a1=;FED~*lj!&9f_4d(L3~wd{1(c#WCDfQUS2my?g2BkcQ7T!rlsrusM&Tc{o|V zmqj+$!F=#@8cv_Rphh;o+x}JPvoAt+tMFym+w86%>A*trVQvgd_LEYc%!Ak2{}cDI zQI}23Ia#?bP`DsjAlsS5_dlLS=`cpF3XxCdwNFnQkEE}U zZEZg_EBi$4VpJ}`anv0i)b`1K6#+kU@%8OwimcJPQcCNsC*cXU2< zGA2p>eqiAFN|^nMLHdez|C%|MCfzqeD*O!2{~ozFu^XUv&iOu0_bP}wT3r2-O7`4E zKhbiHJfDyc%kXj3Moa)xuz8vvBOU03I4dc!CFE;=mu1tuMO74G2jurDYN2QnYAFgLpN9q9{>RZZ zIAq7y&t8B8a-a0%y0+esjEW8fIR@{f0RhN4fiC(Va&k#o3>cTpPWf_b^4tEme0Siy zJiZ2#LV~`2JR`rpFO%NnIP)7a35$pl%e8^`HGOk?hT&fnQdUuABPY@u|60-v)N7@J z@$_}rxfyzm`ImUr-@v)cNc3RnPu791gt$QzslS9c)S%xK06*v`j?FPy{5r2oK=UPV zQLn^tPBiw7ItV(5JVF_&J;Cjmf(D8M_wrN?!kJoUHjRxgKYxty4YA@3(bDcSIB&Et!8s=prpGwg6zJtH_QAsr~ktC2<(QHn|fJ%&c z+JtH66wxr!a$E7g-sH}y?=z9pbbG%@)d0k^sQF0+ovB185#4nscps}|U_F=`J)N;1 ztW;;&zLg{WyxeHE-}3nMB+9l^_-*%!Dm&oGqAexyj#X=Sn7`f-iiGw8Gzun4Mq-yl z?Fn*Ts%xDi)H_NYJ;TY(+sg5+DyE#TFtRH{7Q2&pJ-}OIHE$9hBCfyPD_XYp%fxnb>6jq72*Q-)l&T|bc5$e ziEf!C!zf)>FJ@Rot?gy#F>2H|B5?EzX?bDM6If&Z=;#yBw?y$-gOI z&WVg;i(X}dC~>8Dr&vFo1TDHz*&Y+XF(QhtBh8QW6r(xq6G|=${B+wIpYTbgIZ?7> z@)LfX!<-_FF!6PO?U8IWs3#TJIA`kK>7tR9W-RLT8ELm=Dk-M&hBrTL#;?4ly8)GBFJl{JgeAcNftd;bu&+KqG{KvPUJ{@Rf{e{exBRL z`q`daW+yvsdm&i_{Gqp~PWr#hp>NW6z+Y>hf5m^FZ9M=FI0_s)*`ua(`#R5fZ$(Ea z3od8ahk2h@<QmB$qG-fj*1(a=W1zf$H7%M(%{i>S_cpuC`3;ejB``+g; zzZMtx@9p+#cOAstG`QP?0959S7!N?BzFicyt8@hxN zU*&)K>ebT}>;j@@E2MBrl&yAC|UyjvfszEp_U5K?Lud=_OvjfFSk zI?Bl@1ss)Epk6=g<%fn5Us5k32?k5L^sC1D!R8UYuL+eN-+AYD6;ZGxOP^Y@1wgv< zj;gH$1x4@Ule?9u=om1iQQ70;{R)3$>rSZ9&L$Zp?9nu6k8>utng7HtVVd}Cm8Y{) z3(9sFW>A+Km+Ey(tBSG&ws=lOtSs{MHq+31L#C+&)zVIpXm?{bCd5&%bG$@n&ZXcH=51;kTB0YD7>`@xEs1|_Uq;_Cy>zB|+o>|<4; zf>_l9Rcr|j6M6}PsK_TtQYXn`%M(i5Z?*z;(%#oei9un2pdV3``3Gg@a`3FF?3s7{ zpmOJPYYw}CIX(Qb9N`$)dXLS8>eloA2vHWu}7&k^)t1ylo)w4<+D2;t+tY$b7;IxqUC}GtPw_gV+ zMKpX9ENsL|stsKUmydVzFs!&vjRZUz%9uz}wQE*=y^&mUstBDg@GEo-p~53MyZT8K zNqLjPESNwGizCP$$st)OsRv&5D2i*pfRG_uz#(x(58X4$aJobfOb>l$BLPL+y4SZi zKC?MFf|A8I$C&LiY8iD$i+y~MsoN&?^C(vWEO5CERp@$|FeKRkRAjo#>4UNz$))m* z$HJH1bAC5Xd7sEOa-TR$0c0nmFvBoroQFR&qIAx6p7egn`|%E+rm%5AWvBy@yft4! z7>$h=&(*NUmmVmP-Ae3eEQza2cJ7)>PcIjZ;k{S0N^i7QDAOcO@CD=nRl5I5mIyH5 z1rkzJ3%@kYkEDiXwPqn103G#F@bt--T8l0BKJ4`DyMAEYTrgW+mel6(ll*LPtnYBeZZ1HE^QQC&^$Nb0BQ|}6ji*DPNAHru= zKZN1CFVV;TrW1cN-K+~{neRSE-QyqwsYTRx?bKT=?@Ll67t??ITjXiA6WXHp$34Ue z$3xQjQIDCKZGQNxTy0fd!5t6zNF=tfXsa`EH&q2p{^x1hOoB^!sE^c7!iB1Johv0b zf2!>LFIlpESUXUk>igy|g>kz!=1?UX4~ecFoZn$qb`9F5{BMvzUBl7J_ zd!J4sy)sz0AhwUbCa0HSCL{L%uyOYxS>1-UM8JKran`5pDb9InZ&pE zHZ9`ipX+&eMEx6M?f14ocJla;>wl>bSljme@A>?b^xw&6?)zqh z|0Zidz!hKb70s7`rQFMhF1qOByFA}FjsT;_W{l@zH9@W*cXQ<40w1Ilqiq+JjTU>j z6o(h;po_Mz*S7&eqkaT%fqx?c9wMAWIMsaBpwu}+vJOmfM)lkl^qy|%lSWY(;DTP3 zPzP~-ko_`$Jq`AH4(yGvM+M`pQQ}GLv$Bt{%&P&6aX?S;c=D?47t?$#tWKJon~ z8f~F&YoQO=>Nz;Jcu)3vE!Ga^O)9w~As3b!z%ZK@K5#1hkF75V4$=`lZ6*g3xDh|g!^rVHDWy#r z+A*S9IYY*^Xw_}mbfCyq!`yj8AG!30=pbR3kW_jPPg%FGvOqIl%h-AYrUiRxU|8E3 zQh^v9;Q}b^7bwak3b*{L0d5$^5SR!UWkVO$NJ}5_6voKIn!6sBU843Hgor0@#HxeO z?u7vikEl$yO;|_O28Yerc#MAW={B#c4MI+FkY}e%><^MxQwg@Q{FQD9T_1%? z4@d^nG)KgTNK?;gStCx+Ih+Y0FeG5fCE(a5;6V~x13(Zr{s26Dr!z0#tMQ);_+gg- zXV^2BE4qLaxXWEq5?a=MVmvc&OpLcuR7rgSNLNeMwuMmiZOf7?<_PWv#*!yxlCx#fPx%xh z1d{%&dPfQP#8kK*m8B(Ccqb!dXjZtTzxrn)I290Ndsim7g^I;HMs5^>-#d%)1HkibLIvp*TUyEc4!&e4a+ zxkl@O|5UN8rO`X;kq*Ua2KP0VH?XYl0OVMtqv8Nqbi`LqoPP_j)CIXzsaek_*(qOg z-cod$P4Ku1kbNyNXz&U63sm}lA%Y*VUSBZ?oE};OjIHPI?_;h!mgI0 zHW@_%d9m7gRc1QlQV?Nr0uFY_UrkpvUiwFf!T-75+7C$n2mO2YBt zZ3n3m3gg6hfQpzj)tneVG4jnDU^6hN(e`!caH$=nP0`{Ba1*GvrAX8YNJ8TcBBiWy zE3<<;X!(cX~<&r z%%MC?t|&EyOf`HiqDdI?D&B(a%P0{~(~Aof2kCxPAk?U?p`HiDEfexI%Eby!CPa8< zY$3)}T_nxOhuY(LZAR8AuGM_)%($vdIy)t-vpP)c#!Sb$OS@UBTd}U&*Q@k>wAA>SZ%xI}VjSv8ku7$&y$<*yB2 z-m59?dGUz^3A+S8GvjZ3>Ig3ysiNetrWzQ8q=bFd`LmiRn373|ngqd8VqFfmg3WX? zjfwO5M@LyR>k70)Qu6P~rXei?TLo-ds74CzT1Zm*ME+1~%>n9pvmChx7%+S1c7L(S z{=$+dhme&D%A0LTT1+Kb`L?3}Y4*ZFWK~6mo79Pg)0LXEbrz?rjSoGiXfiHtJNO)H z^M7yrgNX3gwNMDEkbTKAwZNe2mWGw01?j%s2)jdTBO_VABXEm4G)Ny-3j>~(+;)IE zN1ScIyUjSQ`I4@=a;i)Si5f~AQ^U=?WQ5gL+<;x3W&nl~371?*$rK+CytQih^ zLmZ0A8CWAIuK(-BXylQDa! z9I0`qtcl2XDDz>cyQ>jDv+EOwsY5*4Pg%eQ6{(xlz_qh#xZS%~9Wty2EJgk3*SZ>^ zVHTG87$y>mT9t0rGZ@uJ9@boMbaGIj-$G_a7A12St=4JLz3R4~9+M&%p`5N>M@)*# z81QF$H+DP0heh>?NtnAm zT821s2%9D1p$m}^3D32X3LI6<>iqzA zedw&}MA@ps#;=k@nE-p&M`quE2R}nEZnM}^r%Z^a%C;j3(vv_2C?oyS@X>OZ9O`|3 z^w#uL=s(dB8qj&~r#@rY)t`uq?^L-72O!NKwQu8nyj!TEa2lwYi1ScwGASDG0>th6 zt@+#E5ADWXK4t@1N$$T+8OU?lD*N8k0%hwAeMZ)_|7VC0#CcJtGFP~GS$XX z9dEEcXEOWqlWQei!>*4$Ho0Iifracgn=m`q zkxXN|cIPt}He2^G2r4oC#MuZQ6V|C>T-_?1lwKs69}pIPz6KPo-Ma}Ms>v^2$U>r7 zTashmqs!8CIAuo$KYr`i1r2_qNwGbe>8vkjsb3A-0m#3&=^Fh02_!P#4mwO2i&!+l1V zo&T=pRA6!D^E+w2$ZuP$e659V){m--J@bFaE%ucvDVw*y9ZcenQHDZd}w;F$GV za?_8w5}%v}2t1%(9cTZ3n*A*)+5Z>ymg>)MsPDJL+_!LXx7hBt2y?e6&9^9McW{4h zF}~j!F!tcY-H~wL5zO8BPZyD(-8-9m-cWqM=bIX)iMz-Ade1O--_Uf&g7(0`Ji+n( zfwudeC+>mY{Xt;vp{i;u4RCnN`o}h8ssrOsr`nKA9#iS=0~YsPBk6#ieRA{eANr#q z^R59U#YfHGV&CH)89Gk%=N^$ij~>0V9y?i|%1F zufTY5vZrtj^a1C(b8v#P$F}D{H|>eI^JTSLMSG{^Y|mfOM(tJ6t}~z}0a_uL#_MeR zlAItU<)~Or(EIg&Pf)xFvsaw{f5aT*`}u!~ zw;<@klKA4B@(64krO;}ndT|0keZ3THm;_vXT}@9k8b??^XfS*X)HXCqPz~T~9+|9Z zYN=^Kn_t9{YL+LEXjm@|sXQ(?4L?Y_0N;eICfuGqK0W^*@fL6nwM|{0*l(t!N8|pO zKOvz*&`iKo?=nAx2+fIzURWM93cbede0V(rL2W5tLtwOPbp|x~G{9`@ z#b307?8xFC-Bt(570~fy_%vQ`bap5(tlFmgPf*ColyVkIRTcH+7e_;;_;4bI_{v1L6Dq9VNb%!;!; zF3!vHZ(ta-iwM;!>6E6yi(e}y+b^`so*t58(-__2;!9e>D=%xipDr$iVsc;Y%Ns(T zAlf4yJXej=yqDEp6MA%4O#>l#EP!!Rh3nRBkINd-H8mW^_Pw`nwIXX)LXI6*t(P}l z@|sf<9k+!-6|Hx*`oDWWp0=2Kpw2IytpI%CY^AU$)whGlQ(Xt`QPRwJ!%@n1uHW;u=ax^5@5}Q(2+!!}z8g{P%R%&Wbo)Sz4D0I= z^;f6J<7DgF*OMI60PItiQ5E`HFpxv=to+Nhw_q+<`R=lQ>|duUITCX9b;YUj!$p2{ zROj)zTUzAJ@YeME0759L@O>Po@y7!lS=>9#B2mxBpDVjLceW1lyUIpE7ghr|VIC3CCFSB?I1 zB|`wcjuvb7i}8ysh~xX)VwDCZ1}Ef5vsVl=koqNP5KIk^U7k;l$=|d|Gsu}`M@mT_ zpJs6PJN)t9m=<6I+%YH?7s^D1R#gKL%_A!GV8}Q=g@8dxu?&cXksMxMA+!i`Gc?Pp1`YW6w|QzWE59fnHq02OD^nvmB{&l5#$gE z063|j(~dXTKY_;wK!9^1kInqB4s#;_Il#WkKrdQIKv^iN{DC3XlL96Mf6S~Kc!6!Y zIRYc&3vVyRKPTPJ(XoV?L1_BsF=1c~@f2uO7DTbGqh{!MF4!f$LC~m}d;&mix`gN7 zeHv+EXgD=gQi+|qq9y4)se^$`s%+Dwnh{n?4YGBc(W}UZYA6RuCpDau4aLhX2B007 ze@1EpZr>(P?gJ-SXf!egAP#c=E;a>x=l|x98X_(i885ztFh}Ti9q>p^!nYFv#Gh5o z#wWoQxU*4aDQM!i#yB8Dftm5qGNGeGG3Uv2fzc0H#3L_`Lg1pL)ozvtNYR&a5j+5B zfY8zX^e2kJ{PHOjg@C6HWa2}weu&kdimAlcm=~q+gmIN-9gMu9~MtP?WQn6DfLg*liiC`ic zDZ)}*PQl_IS&{`ePHl3_VbnGjr?i^^OImL7mJS0FyHB0ZJ-0D+ey0k1Z<~DIJ+mwS zu9k!4Yuf5Kt8LU@Be$SslH}XuOUN{PyNcne`mtvUk_MyoFy#%Oe1pPp7fLqk$@#J_F&7-IN3<^F>+Y~VlfmQQIKmbWQ$ z?W&!*>2<~hRh1x9ocL65tHb<<)?Lo?^l9+1z=S_)bH3sEi8#^QdNElt3c;M zeC!_+9F}u={Y;bO-sVz1!<4UU=}Y;jkLH{#7iw8=ZpET4mD-M=Lu~KNm3pg(Dr=%k zy{*n7!LF8CPeWzk!`oW_YLj>2$QCQn`?^OWy{4iJz2)VB`vC1j^+RcI*SU&s(1}fR z-+$Sj=9bgiYF=Ak&o#2Akna)dgI4`tTbi`@$x~9Ycoe)FyQcRYiP*MAm%nX34@5iX zqtA|yjQGkXvJ8sgvrKFveSr}mw{y$n{MxQ1IV{|di$`hjABg=TdW#4OC}>LvfZ zUlN`6!IVAu=7;$CVsksuvMiZ7ti-De-igmb*`$5!#e;^`pl#_33wWCH&TPxL;W&JUJQ7ew(WEGoq5_2dpVqjPXTrkPiA@rQJ1Y4<9h zW>+bnq19NnC{CXiKT-M~BxHt9@{r$xsYG5A$plO@-wnchxpHZ9tOKvz=JJn{@)4t* zUbInFGOtz+p;ex^NdbxXzMci^2G2VCq@LdIj}J3!w>z&jL^>MO#nGC;ZzIH0DQXNW zl1wXV>6&;PVdOA67<@l<$M{f6IfBx(^+FDR{P&fV;&o!iEdb!M$vR`9_0qKJgCds z{%QwqDuv*7cSFW9!<8~{>7Cq41nbxn?RC5nBwt=W*r7ZbB8nBKL>>sngWyHEotiLm<1n_l36uu2ErYsrT)2`!8>3P9IAKMne9d5s$1j$% z{2{%#G5%aJH>=Ua9x>~(W}mGw_twq_@UaK5F*NJW$NaJHC$VQbG8eKjP3y60K>thp zxEk)b9a-l~UduBc@Y{>Vhpb4QSG*x_JYommMteMJg%#XuJm!Q2hCl-DhKoe6*MBB$ z96?6{X~$Qb*91xgO$vcTS^{Mn??gt(H-?TxR)nuCuZf%pA{+uqyct3~-bsQT0seO{DO%npmi1;?T5REo^zMyrVjsxSqqKHgVIA(v$z{2zz+PDx}Bx z5b*tYjS3(LJOQSKPNaNlp1bS}#XrlY`FEuMR8J4qO#`2&ddg*hHqy5r(je!l*$64Q z9U}SO(J$8Nll{>v{Fyf98NcbnNmCdH_^{zYKe|-0f8xuYYD3KggSo#LN~Van3j}aI zi$h6@Nw{P537}Gf0*LRa+0rtk*T0!U;?})YI?rNi8PZB#!6Xr3i=KLt$sVdQnCjZn zbrG_Gl3JkppO2w_lsx)+~4hsO2DXj-Z};Ls(8c zf7eQzV0=WdS6Fk%`h0k*U4GST%u0D_Ah6Pms#35MwJK3TCrnr9OkF|+c9a}zT@24r z17)G1klh_j)Q5o;u^A%r5{@N{I`u1LLq&bQ0AKfv7>iR^G9~!Q2k7Hf+TaZgI0H>j zrhi&c#fQ(61}5iKM1=7)gc|@QjdY~p-oS-4Rrdnq2#dOiL>`jZIWf0#>tgl4T~ThS z0oX2;9t~dMIufb>C8zi}#WsG-RjKSmO`Jka-1T#oXh2{WEycDd{@qm5+2X_HT8& z2nF8W>G2bwijE5A^R4M&Kc@6|yY?WW zc76rnu=mop$Z7}@Z^(Pb=tX-XK|umj+W~$?#ZtSwZ-;GGhb~t~DMShIN?*p*iLUdl zle?lbi?*GYs7wE(GgIQEy4knuP$Q+it831ztNXo+u%xSBsJr{1YuL9tDZF*Ot9#e1 zd-}bb@t}KNsE3udd)c>V-mGW6t0$+ZXZyV;-GriNU#NGat>ehIH=DBetgBb##O~_7 z_k^hDR;X|2r00+Cf8eF}rK@kJsyhgr8faGfacTWzZ#m6NA30f(F4XU(mGXtT3mw@Q zVOuE3yFGQPA7!X>?yv*tqa7h~fZ(&cJ-wq9{9YZ1H26_CV3*a0*IiK%HHcg^7_Hxt zSJlskJcLL!L^VCgb2YdM9^zmgEUK{`xj7it&6LmvdwsXgjR5BoLm?~ppnOh{-7rLa4kO>n0^TOoc|?HtPf!$32ev$g)RF8a+FqWK+rt)^EpK_6s9n zi*(zwC@y>pKsu?|)4=hT@x19;hmy&r4cz^=R%4-3pO#75=rr7eigo0vYl(3mSV*Zx zsC4IlM^&}He`>A1li| zI=ggyJZJlZWp_Id=NFAO5p~D9Uf~~2Z$ibd!QT@R%jJp6f7%5&evE)CvyvwWdDu??kpIzs<@T-y>)S4&BDtxNQ zOdGwA2${+>Bgo|kCYR~OgE`B`SgxnJVz6tY=ecKCl&5Rwi;1hmp=_E%n{Ven8d}!K zL+9>l(s^nL0Bb9o(>Y%;aN zK1}O}IhLy*l?`N+k*fZ#ia^cAtZJ7XKKr(@ND)t$%T}0}VTh7VzsN2p?+lB>E}!2nPtNZ27@CLv z%13;g{3QeN$1ZHn-=VFmy-y_;V-(T-nY|mO{V%)SP__Ho*ZcVjztDPF z5PEl)4F>sqe>DiVk$eo3CU%nkpmc_?q!V~U?)9<5kBPZ?c)tN ze9k%)6zvnMJN#YU!-_>7LIyAF`G=fdgkMFo<`b7 zqnI3GbdECn-=wqs{|a#kj`xsGXE&{!TwI*Tja($R3MbbaDW^ha?A&NUph{k66xp6enJji+l&_>$ zOyT?zcKzqY+8x^L!Phb8jE?M&j_Egi(-rETxSXM{64i7aK!vwRU0N542ZO|1d5>q> zx_Yq7)etOghvHJ~7FO6YJ=5;qzwl1sG-`Uzez1xJe5@C>=-i?iFiZ=0dxRkJr_q8{ z%AkM3mnn)2#tXmcL+J3Oc-Rpyzo{;4H_Z@{NT=?k4cJ!~6+Nrn*QX4&PaEA}?kCN= zKJ2e*a^QCj2?g#C>iYIZ0VrDPWnY34o~>v&l)>iKr9= zItrSqy2{$>`U)E>J4;(@dyAW^yK9z+%KIzm`zj1vY<#RsT&z4;y2(t)O!0Y&Y<-QL zt-a0N?foqa4PHGfK7NiKYo1KIzW$sZFF#LTZ-0+J_?{o!&i@Z^-z|Zb-1!5DFrmVQ z3>$VMh!3JDhZMa@EO$`SMU53Zdi)47WW$a0NG38VGG)PRC=VILs4}L^nKWyL1KF~d z&7G@qvho>iB21n{iyA%J5s~OPrNJCIeF`)VGeKUetqXlRSf(9975FGiB2P)kaQ&*fs?uejmx!6COM426sC45&ndqH*M&gVnN*dCHijp>p zDN%o>S0?}zRM5mApEmJn2BL!csUxNgA_OFZ6wnAYqYm-_0f&70z#u^gKxzd}x#}w; zp$ZZ#uY|Np2&|0Q>S?izxLU{qPQ@@PuDbG?sw2QQ5~{S0sEDi})(Wy~uc|IZC?a+g z#!TELEl@cXs8LS$en>p2@j0ONzi-uE=417I@X zl{CyR1*^B7!Rv+4PN~zjL+Vc0Nv@H=P*a~iUPR9U+xltvYg>ihr$D!s}A=%d5nkR@MT30Qb`iX`<@s-$q(;o zl4(E78p-BpKwmYlZ%PQ-+cJQ#8)&g%F)JXf3a~>%tnP2S6hy09`KnyqQV_5z!V$sd zukqb7m%N4OB>>>qFklihSEE{H6R-F|Nk*=ViO8g*KnYGuf+R*Yio~uM;Xg?74_hrH z0Ns$KKUq;tiv`T%A~5(&{0*X5kZ@<)vSqA$IwEKV1e`q=I8b8gQ+M-w=i0_s$#h)h zkR2S0RlInL9BFi;9POw_KMK;2igctTEvZRmR8b3&@REth9vC;Nu9R{yoHT8YhXksv zZ{T#Heqrg9VA`pg3iUXm^o~#OHB&kbb*aiJYIlx$xugP$sZ>-Y>Pn@`DXiM7s$9*D zQ?&zCQUdj-T`enYzM35*;mD#kGOJt9deXScb*^-c%3JS>*SzX=uYB#RU;hf&zzTM- yge|OL4~y8uDt57qZLDJ-3)#pSN_Mi8t*m7)i`mR-cC(!AtY<$9+R!#O5CA)Ry%G-q literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/orafce_documentation/gif/DBMS_PIPE_flow.gif b/contrib/orafce/doc/orafce_documentation/gif/DBMS_PIPE_flow.gif new file mode 100644 index 0000000000000000000000000000000000000000..601dac9b5f3bb107f1c804e5a036efc4001e1165 GIT binary patch literal 12527 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 kXPf=yqnX|tYybcN literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/orafce_documentation/gif/DBMS_UTILITY.gif b/contrib/orafce/doc/orafce_documentation/gif/DBMS_UTILITY.gif new file mode 100644 index 0000000000000000000000000000000000000000..e9a02fa16d3e2b5f76a62a7e0f14ac4b21d07335 GIT binary patch literal 2178 zcmW-fc|4X`8^4-6HWeyw3?OqtN1QxxJ(wOQSsG zkX@ELm1VL{H4Tc#X(ZjW(M*(>ib^#z&AjJ*>ils&=bY>FIlt@r{(f#At`5t;nW7#D zkpRYjOjZMMhh?BzAKs3BH~<2`00e*nFyH_<1RMsA07rpizyt6IcnmxNo&wK601yxe z7z6?W1%ZJGAR-Vkhy+9mA_ECPLLgy~2uKtprurKg0)~MRU=*0@qS8hPFam;rs%nS> z#396C#1X_%#4*GJ;t}F8;tAp@;u#VE2?z-o2?Plg2@HvVM1(|)M1n+$M1~|l5<(J2 z5~u z@|g03@|5z73P1%!1xy7(1xf`*MW7<0BBmmtBBdgu5>N?I2~&wsiBgGC29zPnFlB@? zN*VhQ1eF^qB&xntMO73yU>sr`W*lK0WgKHXFdi`;GoCP>GM+I3n1GmonLwC8nZTF` zOhin?Oe9RCOk_*~CLtzaCJ`o4CVdc71x@9)3b|_TkK!Lw|8di7fp=O9+^Y(%#%s>D z+}vJO*m6|Y)bq4^bx~XLJQVSuy}J1NcSfs|7I@T@bfjDOikmxX&UBu#4ppA^s4cyb z;}A9aprf{|E8jU@d!gs|<=w^Z>6Sst?<;!BzAE<2^}HxbXZ>p<`jr>Y_FdT0lC*Gb zUFF@{upTj}v+mse`p7|L?%MkE{muJckM?)gUwC*$m^97MtD)-GmSeNIVA@bUcsFEqZ#UoDu8w{yn^9o0e5W*No{t z{)GvDw<&&&U!Z*^XPz>3V>K?Eu zbxV$JMFbk~=Z=GgzVpC{2zD$;TXIx2?qHF(|dp@M`t5#?LAtBX&aqB14+ zwCI2TT#;Flw#O}ecZPcGQvVzrySXel&cN^Z(aea^itL5NQtS+R9Q^r`Jq`!Y-f242dw*l|Xo#7i?4@}ENqZMix2QNvYs1VF z>Q-*L@X0`0z^Y&PYqBFL24>~0vz?adwoLw#W^c2%4!_mp;<;vQ$9=b3LQF^?2yM`RUvQ{#p2gmd~l zBerPUV1?qmtx4_a9ov`wBO(9NEGv!3Hv2zZbGDaXz0{RFtGBqyS^t{ng6orFb`;9D zf3^&{OkH_tmy7*h?j~*Tqf_tAm{hDdKkr0x>f7MoS2P;iRPDYy=HlHsmmBh4l)BL7 zt?Z=yn3ax0s-`LW_Djr zSu{|0CG(3i2bYVXIBHD4DAU9Am71Z$(?_$T?T)4N4(?yAhhE$>TU9p@TcF4|yuOK> zd8o1OPt(b4&2PH`9=n(51y*ZE`*Qi?yAyM@uO{!=Zklmh|G!T0?$*%_#YPXapICPu z(}{c8c-NDCm62AiHqKjk6>4wvw>&c5{_liMfdx+XEpPO!BZrI&4(0h4#ohLa)e2A> zyyiReM_E_MXtB{ra~tcvn->2`X?D?^>-NLndS-{cYZTLDVaty=hfbO5UUVgFpX0ru z$WVuQdR$IR|cgW9%+&-JLS|K>Q^gHx@qVr)a~ zT7v6uNh<@E&N7xqYYso#yE>;&ataIDyh|Is{Z@Ot8Ry9r7!<>#i^QGpAs#f=*6G? zYiMYWtD?gv#Ihr|Jz?u|n@6^`y(4*==e$?i+j`Ha9xv2QEnlOzc*&ei%A!1FxvRah z`*`d`vC)hQcQ?;lC$lF?EW9f`H!5#sHcgzdPOb3T>e-WhZ=%%UMupE_Wlx^xVk2<; EKjB3sH~;_u literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/orafce_documentation/gif/DBTIMEZONE.gif b/contrib/orafce/doc/orafce_documentation/gif/DBTIMEZONE.gif new file mode 100644 index 0000000000000000000000000000000000000000..7d25d42a10db55cbc166d3d74b536d9409ec56fd GIT binary patch literal 1684 zcmW-fe^ij=6~`}0C~6Zk3sIy@7xL_AbUoSXY$BfIhvM4wRdQOPbx~6@+ZovuUwT- z1sA|Ya0y%nSHM-U02aX#SOzO#6+8eB!6Wb(JONMrzd;d{Kp9j({fn=S2!y~0;@8js z8bm{A7>%G&bO0ShhtOek1RX^e&_#3!T}D^XRkVN>(GprlD`*uxKo8L)^cX!sPf-CC zQ3;h%1=a7v9~%jXfCS2pJ^@$U$<5941G|QF4J?B$vo#a)n$a3uKWj zk!7+%R>=eMkUS!f$rJLF6iAVjNSRbfeFQ#nWFQ7+5T6JeV1sOk4YLt8$_}uD><~N5 zj?td-A}g^ntFZd?eX~)31Xw_P zGsJ)x6hmTIjEGTjKpYf@#9?tn92FPDMR7@77FWbou^<-3l2{fiVpTj455*(#SUeF= zML`rrNt8uJRPP1Bcf&{G_vIJ;C>T(KYDf*M5jCm~sDtW|I;@VUqw0dXs4l6?>WaFm z7Sy6zQp;*Zt*Qs=p?ahqt0(H+6Z3=SyX_Kiw{C^S?j5ay(hOlK5gr$gwG1=cSO#%3+g}nbK@VMUfhtIaH#03 zozJ~kWT3SzE#E%~uw9T>bRB&cM>B*+ZSUr{%1&XBbaElYiE z^P!(r?h0)V>AH~c$fJQHQWq`H*}bjfz)OX{Yr9^%CpLO*)wb2~8{-Cl6!-I`*&83+ zeJkR_XU9~=<_t`#`ui5Ads2RCSoY+sX9h1jxBHo(kj1b3xGwp(g&VSl))vG>=cIDz zrK3kv6BZQl;*WyY)R)Azt=o5H*3^gRyuKwp`rRv;VPlg2yY`#B;&Z=Qn4WPiqu}ni zU+uZP?)%7`;H2dnFHW=dPmPP6lKAYovH=y57h=L1j{c&crn|an`^xtk#+PY$a`QuV z)km8){NwJG=Cbs0UG+Jf(s(wOTN6uDPQ0AfT(oL*Bkv90I`HwZo5#kln7XLCA?5g= zh7OF%iq4;Lv9xl+SW^@GrAy28QrOu2@6Vc>N$0y--3}{g$-lPf%g3YQy%%cA-L&{?<%_~>E@c`o!3KhgZhm; zvS4w~wXW1*|4Vq%D}5!cd+F+yq|=)^KM$YRI=_Ea-it}IN_twuhMwq|)_?Y~@9Wd2 zoS6T{c`EC@lsjbluP<*{_oHKW%^z0Y{<@^N?Dp0DRUNmlm7kn&=bOs2EAL#dzFc;v t<1alOcW%`5pV)V^?yASU|*+9zR~w>bKJxqx&qG(i3oug{txxnL74ym literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/orafce_documentation/gif/DECODE.gif b/contrib/orafce/doc/orafce_documentation/gif/DECODE.gif new file mode 100644 index 0000000000000000000000000000000000000000..da902c2e309d76e5346a16a4fd013d5cebf210a9 GIT binary patch literal 3389 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 literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/orafce_documentation/gif/DUMP.gif b/contrib/orafce/doc/orafce_documentation/gif/DUMP.gif new file mode 100644 index 0000000000000000000000000000000000000000..dd525ff9faf9a7a4df86d508ae3538b4dc240df4 GIT binary patch literal 2231 zcmW+&c{rC@8-8dfdkfMcLcNvYv`wohyVCMDNK$eNHBw47KHd;wOi{n;lqHenRY^j0 z&7?Hq=k&GBTM3Dz&yrdAwSS#?>-=%9^PK0p&V4`kegAQsTpZ_pziEZKH|zv3{V-S! zz#XQ8yuRKgd_4dHzyJh*0x;kJI0PI9jsQo2W55IO2zU%U0iFWSKmZUB2p9wc0tJDA z2p}R5F^B|23L*muKtdp4kO)W=BqqNN3<1Ny2rvpvE@W(k03#p>sN6#wAPyl8BaR@B zB90**5RVX#5l;|L5zmkSNI*!yNFYd{NMJ|=BqAhYBoZW2Br+rck`R(Gk_eJ0k{Duu z7(xsqMi8Tj$vot-5eAHbV4yNB%mL;Q<}l_6<|yVE<^l5v^BD64^Az(83xEZL1&jrP z1&RfRMZhA$BE}-YBE=%Z5?~2o31f+1iDHRi2ACntFlGcZikU2dtT-Zo2@nEQR)las zI7B#1I6^o|I7WCNJR&?MJRv+KJR<@S0TBTcfe?Wbfe{gih=_=ZNQg*@$cO|)LPWwu zB1ED@VuS%j~I^`PZ&=b&zJyAKuo|)AWWc4U`zxiA|_%c5++h6 zGA03&5R)*I2$Lw2zJkd?liijjm*@Tl{w?+2w&DHoZiI>R<+C?qhZ{`ut-G9Elb~zn zn(kb3?)PLv6x>`_l5^+CB!?sumn*sT$E?%L#}as~L7kbl9g35u;vLo*@nN%u-tk}RslBf~?UU{rLt$;niSvqWRdMI- zpEf$*H`j@2d=;nPJ+@KNbH4EP>JW?YbIX6s4D8H|n;==*k8UrY;uAZjyTCIcW>i7Y z{>8zCD^ruo3(hM1x`S4b1G9p>Q=)={1Lx~!RNgDtqBzi9^xdJVXL-k;59_&TU9Z$D z3aCu#DYo2n*gWmFR!DD2oyD2li`8>N(!?P9o(rW9eY_vMxq8EIL2mieTgr<+x9$xI zzBVC^>pR~$`BY!!FPGf1uJ`thus+aVn~Z;b))w+-`6uJt&>J>iG&8E9X#M3A!z^j< z&B0uxUrqH=_s5%*>$ckj=&p(k^EV3J-kXtR;hU~DK@;0u{6Uo`DocXROG2yqlK$C4 z58vK(X(nf~I|Ti(XSef`iY?C@%HE`-Lhm6Lrv}Z~x@^`C#|1m|ZB{P1iyqB+a-s9V z$b47*tDpZq6!g%?&1a6ALr2PIYtpf0-@pA&^b~R8 zQkBsxTU;%rBRZY8uPKsu)S4N`}hYTTg~%c+rmt3zePp*;ST4DA4QoB zT-}?rZKGv(L6)sDQO|$N=axy=RcF>~e|r5YVALGN7xmVt6zg5<<{cwzy>7IntG%dd zd0nwT%Hr?+ey48t&l%aJPE9lYz%|{+bs?FmK2WAZArjIoKSZmirisO#J3TPkm7d|8&)k?NS}KQhPgj&rT8v5P7B zYZIQf&Oy^h$KA$%<06fKDx;*E;r+>zd*jF6jKAD@c&J`ma&_D}7rTJSmN2gYef`P- zkKCWYzf~t}*Xe~1L!DMt9gORADtjNMyV3R2hKWY)ryuKCN2!Vo@MmP#JT)y^LvPTK zew=D~>!?ScSzVjsJl((iS5C@98-}OkJDF%NJCWnwF>1(F@wc*>%eT@vZI=(OUMn6d zjJld<@7P>6bTu+faJQJrrQK}d4Q6dy`%-)M!- z(WuqReBJbVZpGde$L*3GdmNrFSTn|RxACL1Lk?Lr*|%raeLj}0qq0~u*62a;{&4dz zt4#ja@z}96aKL-~@~&kr?d85F3bWgr@#WP%X|b7GqUYP>tQZ$C&h~A1er$Hv6Q$K3 z=ZsYa&K~0GlT(y`N^{srFD BMf(5% literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/orafce_documentation/gif/INSTR.gif b/contrib/orafce/doc/orafce_documentation/gif/INSTR.gif new file mode 100644 index 0000000000000000000000000000000000000000..cbbe40b235b72887c89af9b48257515566985d4c GIT binary patch literal 3448 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 literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/orafce_documentation/gif/LENGTH.gif b/contrib/orafce/doc/orafce_documentation/gif/LENGTH.gif new file mode 100644 index 0000000000000000000000000000000000000000..429a99bf429075e06fc432d9ced64c7db26e8933 GIT binary patch literal 1812 zcmW-fdsLRk5yl5JMDbE0Xpm^oMiH|fk2t1+R*y(iKhU(A=aC zg|G1>;AOx&Ms$-11O+1X78Oy(iki2GN?PkB4H`nKl^CnjSND%SyF2IXGxK|%ak0OT zjGEo(GYrxJ`aW#$126&xfxDi*t$BI?1keBkzyJaYKp`j%ia;?a0SmxFuryc%7K0^V z1K0?*2AjZUumuc&K`n)O7{%?w9UC=h4WxnHwzLARP%Et!X~kNJwm@5`Ev+rm7Hdni4cbO+Yi*OZS=*uw zXoK2lZAcr|MzjOkLG5Vmkak!*q6M^|7OjP}uof4AOB@L_0THlEgcOiMQkoQzVp2jD zkcDJvvWP4uOUMSYk!(#ik@YjR0xZa)S%`&Mp17c8&7DHlKjEDo`pg39_5{JckD&_{wb=yVm&izdMS?Zr{9R%7s_VSeO zIG)`h@YU(Hr8`fo33w%Li>GYYsoY>py;fURbb5WCn4Dg*U+zAW7yg!+e&)+h>oyHZ zI=dyde9yVU$c4?<&XgCQ-##YWzjxe$y%&l+d9Nm%J+SXm@x-FIt#Jp9^VyX0)TXls zKWiwNbt0$t_=?XP%Tq3ygu03^u9ieV?bh*?`?lm{nRhInYP()bGst z&P^++j4tLc9%1yie#_&7=I3YKT33U)4<3(9T5ziujtY%x8up*2I%+#IVfe#U$Iizu**_zrCKeLIuQn`AT5`2v(w>GRORC~-Btm-3!jW5V)6mRw&yNXf z-;%v!db^gu#_9LFzc+K~)`9u!f_wka7O*C%tTfuj!t0Tw8qzd=k@vXy3G^AQnK<3hx+VF>H9~&ir_6n*U!#um)&ks z4JE%gc2nQ6DYH8ETmAgG4Y8vFx-E>cXZECe|G2Tc!yxvMU;nZ7F!5K>Bm7t19zDC| zUh?+EsT;rB=G%K(;Q9_*zq=lta@i9)EN@77)8e!dl@oU)4b6|uO1vUd`;1-IV_L)L z5B!6sm(_h!UJZV&!M=Uhn5yJ;rPqm!?DV-gmu836Uaof=T^ z;jg3iZtMEDRRdS7og5iZaPDGzh&XGj`BgVRQY|?jtuBl${dm`fmg>8H)gd7e9$w#N zb4AY+T^>EC`|;=#zs{|xcQS+1JNwjiH}9;CogBPx+lJ=5qZ2*vzq|LpzwD!vkIw{! z9*qcw^x9Y2s{ghAzz^|BrFo@y7wjH%DR*L-Z(8_Hljw~1es${sW7WzYLGP>?rQRA` z^~R$2lZ&4F^veC31&0@<)$AQw7@iz`wBNk+#=keuiH?~3`5te-%v)ibJ4tiE;l!H{ zmHETJFLrNNkkMF6r%K+u+~@R_Me|hjhMI?MV|@Ilq@9|(FyldVdS-_g7N=GFx6R2O zFsrZ_?L#gS3EPdGRnItY4C-9ekTJK(Y>cfiEvEDtYa&r}k0J z$e(*f^gP^>x+L_?pSSr8)htS4l9$5k8Y&hYJKv*wvrbYJh_*f;_Er)`^)(# zziXYA^{74OH==cUfOkjx0iNUj(^^*q^A5k^wa2?IYR&G`d1r@lp0B$Vw5|-Fu+#rf awO{u*)ViwE5bw^;$(|D-tB3S~k^cwE(Q7sU literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/orafce_documentation/gif/LENGTHB.gif b/contrib/orafce/doc/orafce_documentation/gif/LENGTHB.gif new file mode 100644 index 0000000000000000000000000000000000000000..5b594ff79a9b19d54b38f8c0042799c9a9e9a29e GIT binary patch literal 1811 zcmW-fdr()`5yw|01_(MR6(2!VWKg-rYBwD;sDYyVcqLJjTZo8N3@6m+ptVu7uY)8- z0YL?UHK6>^pk9N5AS!9&s~Uvuh?*P6SFNH_wF&sBw!*l*dj2?b&e@ss-Ti#OuGr|w zQx@LuH64}#jQXEPFMug99-Q^!E$77n5I_MC00RgZ00x4gz#uRf3;`3sL@*VY1SW$i zpaC?3R-g$qgBGv=ECfq|MPM;l0ycn+U@Nc*YzAALzd;a0fe;9TI2T775flO;uv0?= z&_Fa48iWR;A!q`ch^9i5&}1|PHK0b+3N@i-)PfeEg=i_X2rWiS&<3;-ZG|?W&1ee> zpdgAuArwY&`f$cZ1xf)aV5co*fHF`SN*SaKR)#1Ol!?kz$|Pm7GDT@n8kJT`lhUlT zC<~N@%2LWAWwEkE*`RDxwo*1Jo0Tm}KnW^QN=OMSaS%Afkw6g;0Xsy<05XsaMFx?< zWC)o+CX%VhBr=&yAq}LFv?5KUnY54vWFc9KEFz1^60(78BwLY9WHZ@90whSHNQi_< z9Quyg$eo~hq;c6&s&_wcxe-RE}16osa=H0-&sYu1jqinxQ8y*H_T z@wJwNdoS)^a58szeB)P_>eDY+DXon)R}L*}Z>fkswC~#2nUC&WYdy68`cd8IwZMeK zwKq<#^E2ji*#aNfI68h?!jS{FTg&j1fAEp|th~bTyl;9|r~d2Q?)Uf3uW5QS{d;TC z?_bfSjWe%|S?>C7cxl+o>%m!z2K6sJ9MwKz)0hvol}w512yjPjDJW@*yg#_E^Fm-r zcx30%@PX22`{t=hU3W$;e($I~wL7OXV@T$RzpQ-n@W$PyMEc`Em~^Gr%wPTPsk>o* zO81Rb119xt4r$Xtk!6F{c*g}VG`)spp6ds`k1BL)N*Mj@O4>trM9Yko*%1Nn`u}r% z5oP*)?v=JS+V$%6hMP%418@z8B-2?UBw7^@JO9#4pMHG9!le>F!<=WgouXoM) zweObdlDBRbB>QK!c`ticW|R*JNj*~N?)Ds)p15UL@M(A1^x)vF%b(3C&nTW&w(CH5 z!|I#a#gX+DdDt{}*0ivg?mcU~zRVn58CZ4v?vk_#AGJ4LJ6(HXUxokb(2VL|j%%aF z5DNNeZdFUi^y>3-e^~07c5{66f}ow_);FFyHX-q^{%@W+JZ#dKaob+YS~YA*#Ty+R z^=Hp-op`D*d_5+z@%fR$qsgh|ZO1Ov_}69^Rl4(sv@J0FPj8~`A4HM#;mFH}>!GmgIAFbs>v)F1@+$%uieWO7{A;91rz)$0uz3)gRBc#)iC}7IN>- z&WNNtJ=ac$WORI-KY74h--{`oB^#@uxxOT*CcG%V>ld|Ed2N}03%xgC?f3qV54v7? zTK(yc)(DRcCu3)>Y z_b}hFso|Bq2TyH2*7vQhEZ_V}k1;XFz2|nV9m17;!mfy&+n6atC>#efn(WrYkILVzH^5D-}p zG5|6VG6*slG6XUdvH-FWvIw#mvIMdeasYA=atLx5as+Y|@&NJ>@(A)6@&xh}3IGZa z3J3}q3IqxiQUEE06hVq1C6LN^$i{{UAOtW1L`DlS05K3T2r(Ej1Thq`0I?9U2(cKk z1hEux0C5m;2yqy31aTDc0Pzs<2=N&41o0FJ00|HY2niSo1PK&TfG9*1A&Lt&%t6c{%wfzC%u&n(%tOp0 z%wxefiLZ11~3sI_oR$HggK9`W_9Edo79c8DG*+ zIo6iG*!{FeW7UJs6Jv1qFlnrwxRLL8RNu3y=22hicV)g^O|?_~l7PGvx%|2(cN)T` zhV@zupA21zQT=@F#fIl&tp_`OySEyE%o~hz+lot#KTQb^D6QdC$eXDVpQs6&%YHvM zOth3)pVAMQjh!C2zHQyzmxq$ysE&&-G+&PDeCM!W#7-fT9f?x@Izr^AV6OdJ%v|RK z+Of~JEcpC0EHa9>oZTZ$Pw@Y0O+l=Y((+MFAiK~0UAetaJ&vY(`P;8cVt)$j*P9drhk?G?z3gyvBj&g@f`af zvW&Cu+hLClfr)vo`AgClp=jrfuw)0dlj!=u4wr)=k4n5)rO254SoPqbf+Me8M+#C5 zZwLL%ND|}Il_V}k@p{81@#%W}z^;Tvkqx8i2FVsX*9R=;`J&|akAgjcMp4SSn(pA; z=NpED%L*$ePfk@_SmIP{A7oXi7GiMy%Dsy)d9%vn>FaKCD<#g`^o_m%Iq~Yda_qB+Yf4A1dXD8O z<+}$hPnHz&7Hu{QA3S%MZrUH_V=!S>)1~ukwx6?u!n>C}9@8~2&;7>n@IJ4*f7e8O z+qvu~VQXjjE3x#HF|a2uJVLy?`P2P(!X16WuiSjJZ2!=qEV2HN zTtD&~iCq}-_8ylu^k#HFH1Da;PVM`+F*Zx=8}VSSe~4}WN!mX%?iuBq_b^}8^Pk9) z?C#LXdzIyp&E?w`&Q4Wy59dCqnN6Dg@xp{}z;vbD^opcw%^UxHhP{7T>D_Gb^xu~~ za}q2ZyTnm@odoZScdw~21Z zqQ7{*Xp8;jo%*9~?z%=-`W}0RQh$@}|30k$=2c@HH^xT#`(z@d6aPsr;r(fgH&_IYuE;$XvqTe_uJ=a+vJZKi0TvO>Q2bi0N@o2fKQ z=|`O-gCmh?0U7PVeK6L-VyTW|R>}7dzewDdED~Sr>OCFp*tYk){-%r4q|da@nobhi zkIst2{a-DP5RQtNvMn0FzAd-4D@&TF_H&Syw8gdV^ic{O^Jh}H`Xa<&h_`r&^CzH>NUMxU$a9#ZC*{{F|MgM zC2-xLMole{-`hK(;kYg9*60QcTMduUBUw8x248*}n03XVIM2mBS8DUB@9^MM@{sKp z30H0x*gpSusmrfrTPNK5n{Iy^sgjX(@S3{V^_%7M5~FjA$#93qYR|B9Bg^78T+2P)w)S|Ov?;u4ZQ-$r$i7BT z8#()dudbCx$S6cB(UO&~rP6LPDl= zM>%5duSHoj)R}1!QR!BAnirZ<9&<@tBF9D(<<3vvAMd>T?m6%K?)QB!DLHXyTv}?2 zSeOg&=pQXx0E~eC5Ujg*EAAcu0SJHr1VF(9@DMx#kHHi06np?5!6)z;d;wp<02l;A zU>J;mQLq3O!4g;oD_|8IfJ1Nuj=>2y4c-PtPy%I80S$@(8xaVB5hUoL2k0SsgdU?O z=qdVuKB7p>j~zy1QUO&;qvu$%DD?f!|C0y{)yvjW!RD-Cgp<($<}$rXAV6>EMbEy;3%e zE&bros;-!M>PYG4!>b>8a^=IxWm}G}d2FJccC>8kS4A;1Yc?d8Z~J=v(AOJI9WCGf z?Z)_}q2VbNJC1E0yC!OSO~uadwollcQk=5Kx(`#!GwW;iyb=a49b6gy^xlt7lxG~P ze*5O2k587(Iq=M1oAyQ@uWa91#=JM5#CpO2$ z%|5rRs4w-YoKLeBZ~SjW+X3$l{AKl~@dw7`FS!1^vt7zs4QtvpXXoyzH|o1TG5G8% zZY;ZAkDng?VN{{_$&Sm_bwyS4yCuEfCjb18y^g(nX2(BE8tXeB0zKYi`n5S#ZPw(+x*T?Y4>j_s5*Y3Aohu>#I}!ruz#O?Y!gbmWX>Ek0Q{BRx8y za&_KG0Hs0sgG z-!Z>=YhGEe zqH(cD*XEuqI@-VUnltNKKid$M(X&fb=8DGUtIFd;ubsH^`N#*iOs^gBP0088G1p#+ z+S_%>ptG;d?eU+BGVs2bOev43y7({uQt!sRrbH=~)Tx;TI~NZ=(%oHtZhURzgEg6t zQF=W@kB(?Q^~Tr3zI$~-SXU#cI zcyH@Zlat2GPkjI6{rgX}?>IAl+Pm1!U3e~WK=GxLNiB!u%r4&b%o~@N*A3i%x>N3? zrimY|FB(4nz`-6JKDd3)YdwOC`+$Y$Z@hFq7`cEjh2+%M8%XML|)~r zsBBtt9LnlVC5Lh-rP4uPA@lpT^T%8>^IX^5&-1zO4K`Lr#-5H-9YHVvYG0>G0AK;M zfp~p?i~W885C8-K27mxS0RsR70fPX80Yd;o0Sf>N0gC{O0ZRZ&0S5pF0fzvG0Y?Bw z0S^EV0gnKW0Z#x=fdGI2fq;O3fk1#j#eV||0Y!jfKnbAYi&z^%fFQsS5OEDM05T9V z2r?Kl1Tqw|0J0FW2(lQm1hN!z0CEs=2yz&51acJe0P+y>2=W;61o9LL016Na2nrYq z1PT;V04anNL5d+Ikc#^dj|~w(2w((=xGls0#6ZL##9+h_#8AWn#6rX(#A3t}#8Sio z#6iR%#9_n{#8Jcp#6!d*#AC!0#8V^yBtRq}Bw!>EBv3>Fq7YGpC`ObZDn=kC4lzI& zU<`A!ZR~F=h#7Ddqs?Am$L}Fy;v6DCPm?A?6Y0G3E*8 zDHZ@0AQli7Fct_FD5d~Yh$+GpV@fa;(-)f!2|xs30*KfQ!T`cR!XUz6!Vtnx!UDoV z!XmT$qaZwxv22ci422lo6hERr57El&a7Eu;cmQa>b4p0tK4p9zMj!=$L z9#9@q9#I}so=~1r0Z;)_0Z{={fl%SQm^f%+x5dcCbN?s)U+Vwcq$m(cFR?C6zZ)$j zr{!8-c&R#ej=F8Cby3E>6AIA(S$$Dv-APsR<4bIcvl@~#op{%V;>%6RI^K<`HYM53 z=Z!*NKWiw-xu0edEu&;xdZjhfI!Vi|u{5_W$05`9f^8WuCZ2$R_W!uzWl4j>dmc3~&^ga==uIR_WCkjEmUCp;{ zy#M{C+SAGA3;k-FTV1NSFLi&$d>~JcVw6LT@E)0DeL*& zHBPrZR;JomJvn&AA`LJr)I~ED+~x<|CR%3bRPFU2dsn|~{J5_f92mH0s+@7us&Ux% zbU=fC`|TwEl%n0OSEgQD^Ka?QsIU%VARoBT&S+i)1T4)*BwipBCv- z*>9g$INi?~c}z7bUv%tx|7}aBit^uX=v(0A*I!T7JA8e0;PRx|(2dn}!oO1X=WnKK z=l?u8kwZsCZo7|3jPAOV7k_eD&>QP1vgV2zWvjGiP4fV&w%(Z6*$_}wo$j;Ls4Qu) z|L{E5#Zu5Od8)I6TFj1gPa_wDxlut!)hs-Fw!6s7g*}Ki+^6R2Vr?dKC^$;@bKA@V zDW~j4<24MjF3ER$k6BC;rrN zcA6K`k5U~E){S@2?p@GdZpP%#jde6uUu^lKg`T^Q=}@Hik@>JFzshdw@XD;zkPNfM zgIE7B@VBV1EzGDhe*HjoyP55BQMnppHl)80KU%i7LRlw!ugaBq$I=Z}C`dogIwcw3 zw{lU&75gW9E%(LyIv7;cl`mK(mwB|$V%R*pA#iVkWbS1X_cFU?Jok$9Deb4bAgwD+)OGr_sn{;*^c0)^ z$4e^;tRv&hcZDPx=e@8GGx(5frI+D+T~1@EWN2D(R>m6Xko(6U{E(pc*~aH$oNfV; zP6=K*V?&UtURLghq+^-hsUF$O_4T6?U+G_w-&v{@s@!Ndux6JFnA8A$V z-)5Wf*K7xO3BQ9~(l|-EHDj*ZoN%u#j#6Ds4mDN0b3x?l>5q2jZR_{1T88<8nKJ7a z_%73F?OA@k%kNs?NTO>{pYx)KhWve%yWK>5``-g21>v)_cS@{vxjQBl9$gpUDStqH z?yTJwO_5i9)HiOcb6!|{wpkk)uHDugcD5w>+f_gF15e^Fy(z7Cq*B)V2eey5{@6Ob zvVQVGtX)*_{7tSVTUP9`Ojz7G?SImyxt1SR6fTR%Rqi`=TH;U3s{?wX3aJ+-LW6YN zyaM%qe&2E5WYOTGipb<^ozkmy>X@#NiK5#B_Nyb?^SZtiiz?s!x%yyt$1xk9DLX}t zL6x7Iy1$LxxC15&xT$tM5{l(jQtJzk%+~HHk{LRsw=tL>_6FJK95dwydRMPx)%;2m z9qljoIu&TtZI2vus0nv6^p<(J(OJvp2e7$5vP`yb;hCfOZcRcfLfiGqpCO|6e|^{$I2vn7f&Ygp;0dsug=+)arB ztH375CjC~$no@q1+e_!PnzqIG(OsRHemk~qZ~M(|Bz5m9%bn+^Jyv|aqDUif$WHy1 zj^w&9tDUcg5=|6O>=;Tb7o9bnJagdLt34{cV=t5U-5O^)zZl#y>a~9#*)iTV+H&J> zQS*yihQ-F#%xh{{cE= BcS!&M literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/orafce_documentation/gif/LTRIM.gif b/contrib/orafce/doc/orafce_documentation/gif/LTRIM.gif new file mode 100644 index 0000000000000000000000000000000000000000..994275a4fd7dc4681822701d492aab33962716a3 GIT binary patch literal 2438 zcmW-feO!*$AIHCnTcxb0nT0f3$t853Os%1ZNq2dgTv?Wo97$UxDp9+p(4(g)2Py6- zmz2Bc(V9e0j-Sw_MJW%-(rB_uZO-qu^T&CebH1_%)Y=FEMnDiybq#TVID|Ni zID$BeIEHvYJVHE1JV87~JVOE?0U-e+fgph*fgur)h>(bpNRUX8$dCj`LP)|$B1oc0 zVu%4^2r-NpL5w1%?n6B`!hkUl3{>3~<^XdDa~N|3a};w7^MHATd5n32d5U?41;7Hr z0>%Qt0>uKuB480=5o3{Hkz$cy39y8)gt0`hM6tv$1I!R+7&C$y#Y~MrO&k%x1PB4D zCPFwM93mVh93dPf93wms9uXcBo)DfAo)H0vfQW#JK!`wzz=#M$L`1|yBt)b{WJCfY zAtGTS5h76{F~Wc_L>MNF5Jm}8(^s2~C}0YN0#%zqIiMV(9Htzh9HktiJWw7{9#fuB zo>HDs0jPkefT=*JK&il}2vkH=#8f0yq*P>70xBUYVJZp0ipsC$fBUjJ;pZI^N|8LW3f&uM`ZkMvEVzrE>`_x@J zUA<3#s{2Vd)tQ?~<56I1ol04Iz|19aqM|UnK6RR>=u=;K_E!4r4Gkw1MdxmxunX&M ztuM;Clj#_%Gs*pO?p>u@>hv`Ym(SnNS*~=?aK9o-7rrkFY-_l3p}AmPb>bwC;=Gok zfcxT_Tg4Y2m4rNN$nYr1Z>xyx>u$SMQqcaJpgG2Lap|Qe)$vB0FD+F)i?;!{Q;W+A zJ8z|KuuV=bE9$zF<&tE&WT@yxZLX)o$lgP9T$;nzUpTd7XI4*2iNTAs3RY44rsmgK zGE2MGAOC8v8#6hxbl3aUUG>W0J!4h&zTNkV)&{rf4|~6PSl##HLsZ3J-_KR)YcqTI zI@vUMd4$i>k6AQiEh*a%c>U)5O4}gp__wLo#>I};cpq_4m}OPh9=qN?QeP+^p&u)^ zX$@Jg+t_2|VsY3YEY`pH(~)5Nck6Pqpa8e<<8qV02WR$Z-j2^o8Wyx5dVFB%9e)yO z{zcrkKh4QbXtRow+1P97XUes7;$2gn@a}aMo+joS_pds&SGIeUj;(g|+SgNM(GwHD z>)dQJTib8maF)%+d5hA%f1VL6ZSHceUK8-b+)w6>PYQkrEG5?xPgE|GV#tdaIZ z8=U&DSm`HYd|ns4brQOAg8m@u4r^zGyX-Qp>DnY}yx*ajD$95nxC^alAz`~zukAC%NTdi?PNOsMG@U~rD>2nuFbd^Q4 z$0?2NT7SMjr!nG5a_Y9h4*T{ik%b+|Ip;KS-E}R zP!?@C^qCbcMMkTJ|NP|qn(Wu5?bIRew}Hyw-hT{ceUn?GO#kPvh?%oa^-;}s zhj({BTM+%XuB(Yp`M+4W41TDJeZ&28~SE2@Yp8b z1&>R2uIgRYKOrIP!)N7r)20b^f35wXYjvN5>_gr?Vt6zES(75e7 z3FR$WPX1FT>`3Wev}WR=CousB2UM0@^}L_1sI+sEn`Q^Z%6udepq|tsPTLq)!|XUWPfRS*kxtQe#`1V^d@LOt(jbP zehIIUX7#4zX2xBc72bY&CcDi&&PHwxcR6W3ZL;4D+cWYnqHIlqF735-b=IBFU)^|P zd1USGAknjbbBk%L7F|DfWYS}w___uQMG}h)j!FG)gT3WK8&AwF#eO*ox2ZK-cUW(C zpPshGztdyeP+unKB~^Yi;NR~D{M;w?#%YOe|k8? zxS-~DMbFV}y9%1?Jkr0`x$@n?r9rtXl>u_A6+NP*>ZN0oam9Cr=7GA84BUSWSh?CF zJLg_p{O5XQMmbL{E?(>wZ7QBVxUM%k!2QSgy29_9=Q%CB(4mpxe&JqorS-}qz8Bp* z{ym<0eJfIpAJM~4{9&#};EheeL9fP)=2jJJs9f?sO)+1n^ZfFLxBB^$p7&rU2j{z9 z{(G14_tk@q#&yK*5m&2fnR%53U+mQv=2QT Os2sdKOQE3w4*v(1bgWkZ literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/orafce_documentation/gif/MEDIAN.gif b/contrib/orafce/doc/orafce_documentation/gif/MEDIAN.gif new file mode 100644 index 0000000000000000000000000000000000000000..20a124ef0f988bf0335af444fbc4b4c33acde494 GIT binary patch literal 1912 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 literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/orafce_documentation/gif/NANVL.gif b/contrib/orafce/doc/orafce_documentation/gif/NANVL.gif new file mode 100644 index 0000000000000000000000000000000000000000..0bd8234a84f9a02fc0d85180bdc8c522277e81b5 GIT binary patch literal 2999 zcmW-fd0dZ68^(t$ZC)X?aYQA*r;y2ZQnHlFuZ294;wUvG^jc1~!XsNsTD+%>7FrZf zwC{N-rOmYWbW|isw4*3`Gw;utKjt$t_vbU$eSfcOi@E8VwN7mlJ-`tF)W1)f0Du_i z0Ll9GcJ^0VRM+E)s1B0fGQSKqNKD0LVbd zAjn|I5Xex-0?0zhBFJLM639}>0mwnfA;@9K5y(-<1IR3IYK!~ zc|dtcc|>_kc|v(g1waKz1w;i*1ww^iVv?Xq+?F7h%>9q}KdJv`lcr!qM$IhsZqY^Q zSt7^s)R^L{3X3cv&C+6@2Pi?0SLJDOWkCxz`Ky_y$5-6cw&fiw((kvtai8XhOFUrod$h=<{XLeJxr!wQ(xz2Xtwnq0%Yk->JigylxPKEEBgz&#hQ z@ZP`R`x%s0cI#hv$DP3NVK?VxHqC+Jf+ZQPXU9f!=AU!*8nHV?YdrH>QcgmTu z#^X}@6YF-~F=o~*R=@1NCXr~2i8~@MPmEFOFdB2!cHYCu>~iu}o}*NJX>9G(yC$+m zAKwKTo9egxIZHP4ol%L{O#ZM=i)p9vPM=cShiRtK+eOf_?suf7)Z&eOan zJL1g57$;2=!ztZe5l51*OaUc*8L?5nhq-;Y?cDAbZEPQX@`L}`x|2J-?SCXXcseI2 zcO9`G*yL(*_}PK(dy$%J{D+FSt;@8LVyMsyX~qG zSQB=#`0>|z_mq%>>8H2f>)UxK&C+mt5=iz zZOIb`M%p7~s&Bu&w0JWppBRB13s+MJ2DUwo9=eJ`#v*D6Qq>+iy&EprS8Up4P3=yQ7ivtd9}Jt+LkqO3zc zZiiK>v(xf3md5u!O=&CWS2g@&^i!d?zJtBG!<(ZPr1h_JX4P_c49$%lUdDuNI9=25 zL-aJ|_Rob`)&{h1$n4Ir%hOO?!+n&xyX(`)3>u?z=4ESnWIbD0rS9_knb@}bKcqPM zZ0nr$48Y6Kfh#} zZaI0zDZ|Ubo+nH;2ToKie@63a7i{T}E-MX>@|D-~n2K*?6E9U|!-D}rb4RScd$Gp+ z_-%~OLdDOT4_>)%GnR|1Iuv$KakfR;wds@YJal%SLCG?8J4ZA3AI`BVO(vF#B8~Bx z&-s{#)7^WA!UJ{-8w|cL*{$=%c3yb-7Lr+e-L3KF;w38*{a&Z~*iRDgxo)ydqd&>C zw52X5*U!SnVye^09g8-|FIQ9YSD&)ezp=PBQ_-Mf#-a;{MJ!!m84vI_P<81~$&*w$;a;@Jox^_3gcrSg*?<33N- zVbtQWQPX0B2d+tjj$?n9_LwA|o;Fb7uKFbKzE^6QL%*X#Z*xb*&fM5}$u47aLyh#Z z&Q)3-I%?nB)OBKJe#qH@T}crR!JbVy6|)9Q8v{x{TZI1?s#fey_V%s$(Q;CEAU|dI zm!vm8&)j(W)2rP(V@~@Q(LiC$v}L|?zm#3X!6C`@AC84Vj0Y`y9hsHeuO1Ci96N0cQ(xBtYw|g zKU*1WU%Gp>m&hgTM!dYv`%;kL`!++=Z2J1vcCCjeveJ(}D>QoFW#wF+6|Eqnz20PM za-Mn60~PjuX1v_R^P=n>4<7d&dwM1^dZroP{Cul<_kqdxx6fATsWWSFY+V;GQiupq zP0Fv0XbxJHQ)oBuqO0=bcIow_MFD@gDU}*;a`OmEYmC!YHarv{w`pczi*AEz-r`xa z=`nGr;iu4)Nu4D&rj@RC$zQ@U*1eCz4KXR$)A`fQeNrKLCGRxjx-+kKjqP?dsygm= z$9pzWT$xwz-J~*O*?4#3O1;L!0mlcn<2{Z?^Q|*)d`-3<@!mV#o6SA9J}u(xGt2b_ zEm0f4MP;7tKQb8O|6xG;O>XP>z^R&owvp{mzss8-rXZ^TiZp~<^ LL0j*H3Bch0lMv`6 literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/orafce_documentation/gif/NEXT_DAY.gif b/contrib/orafce/doc/orafce_documentation/gif/NEXT_DAY.gif new file mode 100644 index 0000000000000000000000000000000000000000..84df668740b7e1927f5219b59c346aaac00f9b16 GIT binary patch literal 2310 zcmW-fdsL0v8pdDuM0DYlj!P<$&vD8`iZiy_c4`$;eJ)9Io4Vk*4O-jnOKqi8tDU1} zC0Sk9w}e_EaokF@ni2VsOU6EOAK2JR4EsIHo`1|S=X}SQ&-?zKXSToZG|%OSdd`3i z0HeEm_WsEMqA3ajpg>TdC@>T_3Wy@0NKm9GG88$Ah!UVgP@*U?lsHO=GN4RQrYJL%Im(C% zph8fgs4!GGD&NISK{L5+LT;M-FY&)p|JP=wgRcI={Z15KN-(n-AAaq`k-8*HCsn>* zrS?jy9f^5(t+ME9hQp63!~Ls@8?(lR$l;AuB~96GD~Xb;&kD1h1)la#iCLP!M>g3ZFLRkl1#9 z#_;?`;g5Cudla60*;*c=k2vJDH{#aOl}!iVgl$=V&#?J*UpK3+w~sF-G);BO+@^XG zmw7n!?ZTRm&s&NDZlq;;`oFfQ9IKqyZ1;QDuPk-c?k|p?GoHUG#j_F1x2L`9*MOe8 zm+ky$_3q>Ce`(>>)YR`9NxST#!rFmQ|~LqHvnJ z#iD=AwAEXE|I7|@i7M<0aaq21_A_rIXr5%nYfA%Ngn@~D?a%kPIL7;z+-J{b3getK zb{otS_vZY>qJy+C#h&(U{-ws`OD~fE-&`8`{}wmF32^UE9Z&7r)v3u0*ZPbkb%!&RCN;;IiHC`oaeDR~GAU zPBfoTykb^vf7@*9EZ5V`=JjPwTYBcVI9CtKx5xn2_d=g{27}$tKm1YX6Sa4(P0pNv3b!-^ZC+AbI%ogu>Vm z>i*$*?MOj-_bspYH({+JbwRP6Htf+R)u|q1S zS=-)Be&2oeEDoKbYOgdqvi^hfpjg|w#$HR)Jnr#JZywY?n3Em!^qFV2W!={enf-Cv zczay&A-_+bHM_&T3f@L2wb$DEZQY~KtDflMvfn!)WK3>-La~ioabwhuC+{69|JVCM z{6onnt#0o5S9d&9M#pX$wmGZYtF*$(tNGu3bPFD}R~ypa`$nw@IC5b@)x?U8gK4DU z9@aQl?if92)cMAvkTNl9K5`^^F)$m#Jb)~2JP#i`~1wIYhXv%BI5Y1)4eWs zwZ-U#+lzLVd+0{CTew!~^UTipjY+IA4|!tV`U0TL&&n;XZ+FB4O`Jym)GW~G(Dz!- zp<|W~bGAvDJk}{Mc$Vg5s^k8ZPcM9FuyHF1S$tUNKOunB>pN~9 zDh^dm&+z0RFZ;(}zD--L1X<=~!H|KBCKb_xzVn$0tY@MZ3H aSB(v5Glq3mRGibO;4VAL_ZWC>}}q8E=yM084{>?8fiLue{X$#!T_QhM~Z zN?AiG)k&i~N{jrAJ;`X%f-^7YkMlX_+@H_6?)!UPb`G|(>1)eW-C;ApsBU!?fSE7> zl z4ikbEQX(=U0g(`qFp&t6 zD3KUpKo}wn6GjN5gemDO%|;Y31ww%;&7d4m4p9zMj!=$Lj!_;ck0_5RPbg0*&!_-Y zKvcj~AXK1KU{nMuA}V4k5-L(EGAaR;5S1{M2$d+67-c{iq6||;D5I3IA3;#Mp+utW zOIcJ#fdj@N#$m=0#!<#G#slLK<1yn2<0<1A6MzYb3783l36u$piNHj}M9f6OM9M_Q zBw!L^5@r%%5@pg4F=fz{ZYz;1=l&`FGxa|=%~$ZH-*EfP^LIlu^~QTV$V?~<)izxi zW1p3HKVlg2Yj}`#q2!p!{P5up*-53*=1WA6((H@nrzfu~i*d+Ft~e*#_P(JsC#5pp zIz(&4!Yh{^U9gWHzpCs?YITb1g@v&Tb4BU$@*Ka$vfRrx8EXo|M>t+hdz#~0Ev_oR zs;JM~{7+e|V_tgWjlj0|M-0@2rUF5&zp+zShDzw3{@iMspY=~k)#znX-YP(xSrd*@A91DNwWX>x76fsdvohn)R);!KKq)DU1AiQ ztA05a&9l`vuWieu<9d!7pXzCAu1$46CR>#-%zJQwV`JQ{o83t-NB+4;MiR3-<<;@= zi<(k)KP=V5x~7w(J-fWR4j!`h{c09zIj#MT+kU4;x26#7hcBPcYaVf;RQTTS*J8oz zQ(mQ3uhYHfheo+%53)FSKhhyY=jvN8x!(At33I))+v=@b$LSdvj!n$+@@dWLdJ$>! zQQy?Bd(`$2%eh)MP6LXD+q&-Cs|ZxHn4$VQVX@ticis_E6O*5=)9lmz(%ScY0zK7h zu9j%cO)hOBt_K}zPY2#M_dMOV!d`oSueBNk&d|E@bYN4i+nRGN1M8A6I7XHD#GQDb z>qGQ*!KLlaOr75F^G*}?st*tRPCZ(;Zb(a<(J+wTvLbobh*w1Cv0 z=8TG03w1O6yR|-KUF-LU`|-F(;U97`#&Nn?#nbz@E>QtaVVlC@8pL5x_N#E z{Xbp{4C9P)wvvdCH!AS@H#dXF*tOp3dVTpfafqbVTChWPX4)O~p({UK?Q1qX=ALnP z`KQ8J)4ygEX}j&#yWi@y@!w*L4ZG7zhKJtNeb97#!spU4WVfQsJnv?DxpvloHu|i} zDWO8vcMQt5dh-FzpQUEhUTI@ArCnjihAOsKrCBQ4D_z_SJE~=iVpF80m4`b{uk>l$ zTC-sPx05Qpr~Esg9(I_nQx~4E>k%wn|GqRT~-n0`CWd<+S{e;MV!%|n%C|l1FE80J1*wD zs55HL7Dehwd(jPIny}`(&n-&BkTZzEz1M)qOV^^zohD zUSHVYGj*FGoEfxTV`*gc3^iE3M`QcW-gCW6YYXT6e^!!fm}y*%=o9&EY3Atfsqvj> zLQ}^M)(VjYE$KOvbZ<`)%)D4SK}6kTgOeGDlFHN*6U{Pu2OkO1 zSUYm7`Tj(s#}~G+Im8+7*1ubQLNswJiqgG*$7=uj{d=axtcmyPb3erC-oD=RqrRs3QjUf-sc9xc}?)iM$d!?qungyIys+piMH{loW zqfg6h?SkGO?yz^AlCUms!^EPH1qU>zczv4ZVw)C_IqLkQ??Y3XzE;Y$J?5s&Hr1GX zJbSCxl*Yb&g0%H@e0Ar}Ye@N4Ha-2It3|BNBb~8thTdNuTzqz~M`S|Jutf!#ItyO4 z8IQEu@u1T?+IDNqg+87a<2udQ|lE#8){e`&FQZJT-Gjh>G5UAj>o zE_V%=S!ygYoo2sgaZAXPuPZhw{`G5KQWB(-_U!XfxijaPTQ})e&*RG@9yKOHvn;tx zKjYx<6Kq<~-gu_cmVMJB<~MoB(}&s4*`Mn++Pn9wt2*0x<&yJwg`Zn_-IME`xtT{4 l8+{z>>*_kM=2s{Jc9z#a>*>t9GcbK~sAIzmzcD6Y^&jIQn{@yH literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/orafce_documentation/gif/NVARCHAR2.gif b/contrib/orafce/doc/orafce_documentation/gif/NVARCHAR2.gif new file mode 100644 index 0000000000000000000000000000000000000000..aec71c449329aa90a1475eff246f2461128d4b61 GIT binary patch literal 1947 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 literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/orafce_documentation/gif/NVL2.gif b/contrib/orafce/doc/orafce_documentation/gif/NVL2.gif new file mode 100644 index 0000000000000000000000000000000000000000..9e2fa34a1d510bcab5402a102945219aec25915d GIT binary patch literal 2651 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 R^CL-oX^x?6K>h)={6FfeAaVcz literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/orafce_documentation/gif/ROUND.gif b/contrib/orafce/doc/orafce_documentation/gif/ROUND.gif new file mode 100644 index 0000000000000000000000000000000000000000..1db6ecd57a9e4f94eb82cc6dff001fcb4f2bd0f6 GIT binary patch literal 2227 zcmW+&dpwp&8@|Q9Ii#(W?SNQ)k;ksp_*$C|FXb3Q9$P+1wk8r4g_3$wgj7P9)aHD6 zR0z`Im8sQBa0E|AU zsQ}o3IVjiXyFH%|fB-N60iXa3H~kcg2;kVuipkOW9VNWw@WNTNt$ zhyh{&tU>;!}W1e81VxD0Euz;|D zu|TjuvB0nhSVUOFSR`1aSY%iNEFmmmEDFpaP--rUIb?r2?ZOP!UlPQ;|@SQjt*!sD!A5sYIwmsl+G)$`ECkGC~=pjC~G* z(ha2~N?yvQG720p4lxchjxdfgjxin>j~I^`PZ&=b&zJyAKuo|)AWWc4U`zxiA|_%c z5++h6GA03&5R)*I2$Lw2K7%QPrgU2=xpM9=;9pYzWm9J`q@m|@D)-7RbsaN5#i_i8 zJ!6brjyRppzqWTGTHCESUGVz>!v)ED&Xt9Y>849WzsAa<=ByunX*%LuRos$e9nsg_ zSXFZKn0%L}zDsp!TY*!$+4839vRfq{1ujQj&WKX^(yFyRO=rqGDgqjk^%tEz-dPoN zOI+T3_C(jY4G)`+EoKv zt&S=%{tvULpXc*;*%@PM^)z;r)$pnj^64t?Ok|e#J4^2SzMrtlz%Fo%>Ge&+iES(5 zAvw=APAf9VXP4UC=vM71X{M=3!`bR!rMLA-VCZi9%xt^88%5xri=8%OqL*lBF>x3?dcp&_{aZPGf;&#ohIi+83*#-TBR zWh<5**A9M^)49C%*x~d^j(SI;AMD1C%iQ=f;s| zCv|H(^D62L>@<(ZHOQs}ogOp_G5@tGp`*VN&kH$P-RkcAU3drc84f&qd1LuNMcvk} zLn>97A>Rj%J{a@;3GK2A&o9Kr{`~ml`C6@tIzFlVY2CKh%}YwM`yQ3o&lu~PwoY~U zg3jfGntE&M>u02dERbcHtvNY5#`{&hm(j-!!SgN6*DciO(6zia6+KQ@B;oFgM*T&! zjBfSS6Pv{ZwPU#^9fzxKYITh=yOFXzqgQjv3*$n6-v{>h=M65^9bC8gk_qwW*{E>4 zkJlWkY=c(0rDhz^I69+ERa!8sacYuf?jLiEtp?>IG&3~z1E>$lTjjE2tGBB@FK9P2 z-fi4v`eXHP39=axHBRQn$?xO=URJ#kTVLp>2gll2=RAIS+O+kyCyVNgntNEQ`jRjrc%Fl*L79y*+;&k4&+)dpL53XN;xy`nr6LPXk%mNi$jEF9+7EI*Uhi7x8Et-Df=d%qdXWUHbkwrYcmNV_U#d&3pUsgmR3+ULJ{>RKvaQt)9Jnd_G9nHBcBILX{?`ttZ&Q7vz+Y(ABF>~XoB vKY6g!$xZaiYPwxKd$7!7pXgoUB8k3(;?fq;=UkI?a_eAufZHOu@_X_xc4{<| literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/orafce_documentation/gif/RPAD.gif b/contrib/orafce/doc/orafce_documentation/gif/RPAD.gif new file mode 100644 index 0000000000000000000000000000000000000000..a35f2ad9759388e69951902c36b7ff24d8999de3 GIT binary patch literal 2669 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 literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/orafce_documentation/gif/SINH.gif b/contrib/orafce/doc/orafce_documentation/gif/SINH.gif new file mode 100644 index 0000000000000000000000000000000000000000..e9e8ea2abc8089f40cd516e50af47ca0118dbb05 GIT binary patch literal 1838 zcmW-fe^}317RL{owq`_OTN=NX{1_km_;GqDGeZ(nXfS**#*e|c>ANejOs!@2(@6Q9 z$}>6?z8a;g42qg&F(yNDlBW6WXiz)E;+dtI#?PHS`{Dj^pL_3lp8G!M^?tt-IUzV? zVMUu!5C`!4rygwp#=>B5){D247Y9H91wa4{AYcF(2!;ZKz+f;0OaK$XRA3UA45oku zU?ErvECP$c63_q|K`YP%nn4TL05*cHz$UO6Y;pbuK@bH(APnMM9Bo8U2!y~+4Glm8 z(NJg*8jOaZ31}jk3Qal@?`#vQgPe*`#b%wkQE5s6;6tC9K3j;1EXwML-1X5FrD|Kr$2=LR)bN0*}4B zM;zUMcD+~UL31mPy3131`%cV?ICkKhEnU%dz2aETh1A~THg}Ia{@KNhfz$Qei^mUM z+BI}>WmaTSZq=UPKi|K8u_&*4U+DT*drT}oRFe~tF=$?8aei&y)SQXg6HjRS@a!Vj zjmi^;zyJKh^36Raoh+y;T3V~;T{>BKtt9SNW%i_!BR9TG`1$^gOC_H-oU#1QLul#I zAImp(Hs*8bv0E2*_*~c@9DMxtHyHzFp5`x#{#{K&V*ke>tutZSv?(*zrLPYEe%`|A zJEPJcnb(%A81mh(Y4KqVW+HaEoc3h;tz*%7;iGpvZQgo$$b?xDY*pWzowImBVfUXt zPHgCxJgV31hslY}C#J_QKUM$7gw~EVIc=sBoeX#aLGUh~KPwq;lEF#2xR+`rE~wQK5M{AUK9 zPxjn!)h`x&`%djYEOdIRD`{G3YD$mFiu#SeDg0N>Ckyg3uLLgb9$B+Cs%K#1grN79 z_Jz*>>>BXtqr}WE>7JX;{;wcH(@(WNv$^jy#zo(H#uUu?|s8Pt0C zkWW?g&DiV*ANSjHrseK!ccpJesm|m?5T5IbDWkhDVM+6yeS)c3Ov++O<+LvdG7$;$Hq+7njYr88hVBoY|{-?rf?n_N^*O*jkf2{#odSl^xRV zh8&zf_3bxmdrV8p-F>R_gYc@J>CJUdW4~QR1xF7zjq)4QrKM!=<^6a1lzD~j^i-$) zf5~mwUe?lY!{bHvw>j(lpA>wsFYxI{i<9zB-y3#`Yu+SU%vG=cuN{?d0Pz;N))aIZdBzOYGp;_imbg<<9!v?cHAQM10+8cT>{9 z$?jKYRebGV*0f>hR(Jd8i1L6RUT;bcW_O49it?V%no>gBAMj0yIM*kjdE=kFz2MFN E1H@r?{Qv*} literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/orafce_documentation/gif/STRPOSB.gif b/contrib/orafce/doc/orafce_documentation/gif/STRPOSB.gif new file mode 100644 index 0000000000000000000000000000000000000000..59d6d81f64f8a7ab6737a93eadd9eddada4ebb66 GIT binary patch literal 2017 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 literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/orafce_documentation/gif/SYSDATE.gif b/contrib/orafce/doc/orafce_documentation/gif/SYSDATE.gif new file mode 100644 index 0000000000000000000000000000000000000000..a90bf12b777e0a87f12ec50c53a603f3af3ee566 GIT binary patch literal 1561 zcmW-fZE)Ac5yv-ZGJ#5sk;I^cCbmNG%#2~RqOBAHLK1utgp&tJhXUC|1dKy8nHHue zC=bE%Bq1yY%cCI|9wdP9LL4oIkgx-Z=6~;w19%c1gJ|7;TYCEd2tWV~AOH#mz#td`!(arAf(5V$mcTMt z0juBu9D*Zo3{Jo)cmR*!2|R-r@CpGSAOwQI5Cnooe}f_@fikFoMwdt%5eR`1B&wkS zG>C@KFd9LlXaOyvCA5rI&?-7Whv*0$qZ4$B9?&CtLeJ<0y*AHVK%}>*#cW+OKh2~uvK=z z4%rbqW+&{FJ+Mdi#Gcs;d*uKekOOgG4#GiMffZSam05)~q92)!0wll!5}6?e#Gn`w z!(v2?iUqMKmc+7H5v$@r9Eu}xEKbC!co2``Nj!@e@hSl%pahb@5=4TEf+&iTD2s}y z-VQ?KMua5lD=J1&FrWt2kQ!DaYE&(#MYW`s)rwkG2kKBAsbh7bPSt~YR8Q(zy{K0W zpaC_I2G$@NZi_`hi`_tB!(1NH57h1&`?6&&=TVe-uA3fHd2B&?| ziH~-79xD5`b5bOKaF_+qS zC~4mIr~93&NEq4iUhbdLKi|5fS5x6Pi-+DCydmv;|79B%4+&X??xEptaoVuiUnk8U z-vv90&kl~Yptm8?u`X}(aBelxzK zvTt|E!0PxRQ}3Pm$b-)$RloCo`9I4OQz|=lyqx_+%g-*KY&yFkp{cTH-GtNmMT5S` zO8vIB?#%WfuT(caa$we~g~`_k@40_y#h@)epR#`X+7%D?l&;Ikx|X%5tvI>3?D;X{ zUfI(<{>H}K!mIU3IRjVbb(PG%*=NMFZ*1gEQ_r7&=JMgr*Cws4zA>vId-Is7<5n-o ztLi^z&Y9f#4Wq97yY*7d%S#S_H~C8PhP^dsZvCii^!#2^=Fa;om27#@4kNe`KMyn+Y85LcYir?(%kMaxny_unQ0ZV?ysgV&F<-#RWrBe>(aX2 VJ!j_}iuH7sHD_165Elo(_&@fv`5XWM literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/orafce_documentation/gif/TANH.gif b/contrib/orafce/doc/orafce_documentation/gif/TANH.gif new file mode 100644 index 0000000000000000000000000000000000000000..115bca8bc058305fd0223a89cf815f2be2462700 GIT binary patch literal 1906 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 B`04-v literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/orafce_documentation/gif/TO_DATE.gif b/contrib/orafce/doc/orafce_documentation/gif/TO_DATE.gif new file mode 100644 index 0000000000000000000000000000000000000000..032415e5fa0284be584d0dbef19251ccd71d1f58 GIT binary patch literal 2400 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 literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/orafce_documentation/gif/TO_SINGLE_BYTE.gif b/contrib/orafce/doc/orafce_documentation/gif/TO_SINGLE_BYTE.gif new file mode 100644 index 0000000000000000000000000000000000000000..e4ce7f9f4e879e889eef43e1f02b5314c7a9e288 GIT binary patch literal 2033 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 d^WMHlqzG%28z~G3uvd~A8kI@B}{{x_sG)Vve literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/orafce_documentation/gif/Thumbs.db b/contrib/orafce/doc/orafce_documentation/gif/Thumbs.db new file mode 100644 index 0000000000000000000000000000000000000000..c868f38a976270085987d0b1a0d6fbd6cf9c609c GIT binary patch literal 248832 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ℑ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�>u5PG6lsC-dM(s!9 zNr&X<79D??pQ1p}y@^8FJBpE!*A(2N*d&mgvhxE?-MKliL%us!zqj%3BQ=uWloCis z9fF9TtV`FA}U%|Mr#REhOJjGtNl*?+)0VF-gO=D*v2K-d2pzy7!XnKWJ}y^J*W>I6p6 ztHSo95P31irvVFQWaRzSNw4Bh_=;U24q`?FbL0gwwUDw@HwkMLN;4e zIKlPngmR&Lf7~b>o6?!vkld%gFPAU%`BThbbe~I#i5;G$P3tw8Zgop?H!2BJq0?gb zhDqSdP+av7rnu@K{O@1Sknss@637Ir1vx4rp+lU(<>b0fgB@!T@BZ97H0Wny7JKfb zzYrdUNB+13*tsP->%FMGxP@goo%y(g=75)`?=Re-aHfWr-)#Blm9fTyliS z(HRRy_`O1)d;Wxh>j$%XXg*q!ndvVd2erT7CTwUz_`0P4+0_rFM$lqI!^b4|M&C;E z+=saw$?6Yu{ktqqfAg22#Upx}8lIgde!07tCKIc7Z;>oZr=IH{xVPY#cQ^*oR&s`A`Wgv2Go z=0Q2<#(;+*jguVIi!jc`_^E)zH_HFm`g!H8YwEUNQ(njo6;5|i3#O8le9BKBy}4Gm z<~26+WgB-!m4kCzBx^kUfd)7NxYI19b$)%I0SDRmYs3_&hz7&pbGs zoES8_v8tZOY=TH1Oqo>un*Ez6hlrPaeVX}pyMI4+h;1?4rm3YB$Zz?% zrPumTm|AZXzLU@!*l{f?SJBspSevnwo^d6=%l_ve2~vnFB@s=px#JnH8P4Vgt4>>RMdg%o=42C zVSN>{SReoDY=9n`w}pj!vU&#wQ5h27^{TYM%)H|Da^Rwyon55-xV#S{P}r|zlWTSV zIk9S)C-N7Y>V>+BGVMo?BAzu!fA{c?i8;Q0*XQX+9UK(L%O5^agfJujlj4W>MWra| z=NGjxU5O-KK6Q>U;(9jPw@n9ly0V-&e{+72Bj&t#^draK#Q!(C;mLqYlJH)mEM!8O zR1?^xAvU#U{NKDG880=&G55!Zy4`orZ0TSS69O5T@iSb~O1hYro{`c+zjhwJWXm!)_u?Wwwd{1M zd@~<)wP5v8!-8$fS^vE~>^(ZQBsA61;Ew?uY~C&go(8N`i#0SVeQYQ0cnixr9*g-7 zMk~_0KI%eh2n7=QqXR)qOw5*jC(>0)KMwWlkN_Vm|-B_lWy)i ztekAuZudwpF!^h-5fSvhbNCb%eu7!!KCNN!(8MKvzf_T=q<-i^fZX5Tf0g`~?Duw2 zC;`6+E4g1JhUe#~vFYZmkc9#r?tHGNIAE(N*bed3kt4NHDwg8c82ccTB96`VnM*xI zn`U)6gHyzr5>TtuOH;JDDtLJSq z&+_rZ?u8Yc<>9X>2tDPbMM)4t^DpqRMJ7eYuWjl$Ro$MBdeZ#fy6U~g5*><&D$pL?nPU9$j8l+G{1afE?us7kl&|G&2`WOT?J1Y4x zip7YI_N>b}o7ACj)cn!T;?I2KH}QTBRH4}fl@Fdg5ntAtgQRrNWA5@2gOA}prw6Tr zQJbs$vk;ER_#^O{5G-m$Q;G@Y7q(o_M!Jumf6s?VTNN>6&;RjNP@sxyq&NAjwu0v9 z2D1hF?bfThY7r_J-!RY@D|U_d*T#fy_0lBbMRU%RzAG+_p02O3f!@dyBqyVPUwQv| z`Mvt99r*(5;LEoksnYkZcD?`SB;q|(cV)1?eJ_SmEbpy#kMQlfLjrt!nb)SPVj0{F zXJSY*FPu<@d_6{3G6hkSu`Q8B4Vh>pvtjXUxB%rYvbr2(uBum+kKTQqHRzY+5U(UU z%}Dt4I$?2$`kv~2`pNjQ8#)gk$gfO|PrpNw-*QN>vdJ;dFSBT2`eV0VvG?BM4n%TF zx@s57t+%GWTaDT~aoa%7{&9EjVl6ChQ!1&wNjo1K>Fd}SugzB$-4VO7_YYxakRAI6 zsvLZ*(Up6jpA-H)U#;6&4Qdh{Y)r~#TjZSh9Be#>Bs9oBe046p_f#R<0_qU0Ir2eo zh6QX^Gs?7L!%xlaABL0x(P0MH@08>k8b@`{rY$Ohw<aGl7?6nq9m~-l@SDaX3|^6D8*`9FhMl}*8tvrJBq6x;lh4*%%-VUa z()u>)-5Yd+fcsA6;YwL_1uRze=(*}jx3o6*?tgRfU@}t9BLyKF$*qyt67l(b zDv2w>p>0Ik#i^QIdZWFL7V4&{b3D znYet~-S4lIl6*KhIqP5{Qd~m9xk@#ly8MJO5uabld#YpbTTKTM~Ej0Z{8>le}%n@ zTq`q(q9#sIl26)I5WA|a5zeHosv4DBULMP*SMvGTYRijl=vNf^Z5A@DcU z2!cQ`2mzrW41|LS5DBhg5TfB2173kx@ERT#yIyK>;WP??Dmx0E$5gCeMT@8AcR1#{r) zd|p|E&zHb5_z70PD)$~56}R7KmZ5<5g-O6fE3&U`$ZKps2<3P2Iu1xi34C<7Is3ho0npbj1Y4WJ3MfHtrLkAMz%40M4W zcmfPS7%&7zz!;bSQ(y+nfd#MxR=^h6fPZ3t;P?T|0|(#;oPaZM0j?k%oWu3x4o45* z37!Kl;0=7h3*ZaQrZcF%_aP;vi*PC2qm_Ro>k3=D3BLL8o$`$k}^m0$J3oKL6M zOp_DAt)yFLhfoM87WN`4{_shr+dzL#Szli%a{^_DFZ7qC-mcHwcKTPsYl&FAwa_2N zg~!0A#%in~)jH+Pgs}v5wuH0UKKF#Pps~qG4A?5ge0&X?Ql!Iy5}R5ia%xJKg^Wfq&p;} zJ4ERQDd|ue6r_=qE@=d$ySuwPrMsK+u=ZN#{P%aRwfA@SbtczuzB-Bbo%xPujCYi@R<k6afc!>??B@m!3&(ywR=xVYI3Q!=Hj-FZ7y%j2JGg*`F!D0y0Q=rUP3u00; z-XGbiK!*z|x%b}(4P5g_SMR`NNhmJsd8v2@VB#b((u}B|y(AY0YZCWbZfok?sHc_M zS@SO1E9sXwtVb$I$#KaUb0;$e#p;;98x=^%VQYlhC3p)dn8JS(0R}FCY+Bpf+t^U# zP~^k$PDdAK63(_fT-@Ykx%@6Z#9+XF5L_~)Ue1%0K z8eACOJls0)PM3yCB@oQbD_j{W%GUDvK8PwNLvf9yGfuyX1f^CCv?XVs&eN-!@M?95 z4XIewtIf#Up$r4u#Xm9k4lT)o!iczVVLh$`b!DHLmC%Q%7lEz3jS`Ir8wRiwJH4Sq8>lBUDQ-%{&0RFGiU`3o>re5QW`m>cu9{frJz7^-Zqq>>n)pI zYQQn>oZ$V(rk8PY0`)`2i~VUL&ao6cp`31)K@O;{vGu#i1n=(~22^@)v5IR-X40)y zRJdwK61-gt7wwNn#bsOep9MSB$!1)DybLE)4Sxi?7DSCfUR`En9T5TH=hiOADE-#Q z7AvT)&^~ky$=$NPR$5gCc6Wxxe~f?b?RVc-B3ui2gPg5s&X_WnQ#JMXj6b~oyHK{b z)XdDxgm$P^rt@{5nLktlUAoWkuu?A#Y8CQAu|Zpa07Pw5n+o%INC@+{V#d@iU7|Lq z5(Ae=(hty}gK`--FvXv?8?r9h+$1@{Hj~~OuX-vBF+OHwNG@I^gN`vU-yj<7iquxe zL$b+~WjsGp&H3~JrO9dLe(gvQ`}PsUf%4JyFWI2>OQAa#}9ZnCEdf~ypQCrkm?&4=rB$7=uChj$i@q|e$G(D&=tr|Yw?^A?at{y zXyZHR>0R%wvYP8$h@+ulhw64Pht!}7i;DUH&E+<1_kekAt-Oai(EF`|_I;i|4v1}q z)HWYu6(DSAP~A9=CvwPbXQ-W>o%2~U#uWF8+70$Nfg^!OeCyxt=G|`!CZ~BJelE}i z3HYnQKlJpk)%xuCmN{NuxTe*YJEcMaN0t5;{lZI-L}D(Z>#4pKx0b4j0j=L zI%_9(i#={XX^#3XAZHp%4fTZJglkpmHMojXUYqZl ziArkuOX!3-MryrZIKB)$Tgc6zbZc;(SnxJrHINFBON%%OXZmF_ko>I&i*^jt&vBgB ztL2~dOWWW5o4+oE7czc-j0J2e8FHZ}abZ-NQ>0;5ZzQcx;gP+5mV+)NB+5W@GzUZOjQIFE_ zBf{?>@FV*FjjM5lT!T`!vhc)Mr1y6fXhbaNYqZ}tBT9XG;Ov@C1jA~-XMLjvGNxwA z5B`y8KTAp@B#9+uWs$IG<;1*#|Ixp*0gd^S=KRCo8uTXxI@{k0bR%&4zg_-M^zTt1 z_MH3s`TA%7AArjJ>B|`GmkS(&5%~90=Ya{>{{OANZw330`!D+c|1sx)1N0vlfxU8o zum7REXZh=K;Q6<^|9|BFzX$d||6lgc3+^co~yvbPF3M^ZE4%A&xX3$EfUgJU?Hjab?NvC?;bk`oL`yMN&3x4ob{!3BdZ@#s?^AorcXlQ73 z*Tr{DaXrd?yRNpsMg*RP>R)y&@8xb50hert^@q6NV=scRKe92D^lXTrBy*&*7i_7)weG)uWPkp3 z>DCjU*My3`kN#RUSyomH>Bi$s*{<%Z={Pj!v%D{$%D?<@U+FYs1w_5T1r4}kzCKld zD0HtE+VtpNXG<|Bef8?qlP#u{2B-`Jp;UWDSG(f)=h((zMG?U-u{n4WGa({jBm!w! z8Q*GxHz>%Nph#`*;*$O9h8!iG9eMoJQCGC8mOu=LK}6I~`2nPpM1VJ9STrZFX1iG$ zIkLUX& zyQz;wr@8lM=jU4Pigp-iJ2BB}KSYOd^f|kKU9Z7|aZ4>it&d-{uB%u^jj7%;w?qHRN>$W$y>P!-8K&Y`QCK7Xl)CDouBlmhm7=Hx&YFI}?7p!A zvE&okiO359R}RvO-xUhN=5g=Blf#d5JyLS8SWX?YkF4&hEZsRD>F4|#8XAW0!t`Di zzQl=P)*77bVP9|kwEcZl5p<_7UK zWBHK;72q+ptuZk5^%w8hJ2y-t1WUh}qg*HOYVg$ElJy45L54Gp09X7?nXW%`xLx??|PV#9SkflE$1pxMy2Qm zb$Gh$)3Le~)9MS7LPbSI?FyF(UvP79=!3zLGQrc!CrT%1Jz*#QXrv~*$E;&ZgHn`@ zf?QlNlENVE|69AMmGb4jmbT|y_v*$*r+v?faSC~+Ti!z!;YYQ?j+ko3;zVQr_2eE# zh2BwWSTgwtKgbdUG_92I&@%hWCmNss!ufrFwoF!3_*-1ll@Nvz3a5&Gc&wQAEn0S| zQ>?dht#gg{lHnDAtNEv>$pPJX)jx$-y3@3nkIz3eWch}BW;)1 z?|-QNl&(U^3q-r{`qPQS^`fus{ub`W=pd=a%)+u23?oIa7sPj?xFp;1A)9At?5QNr z!VUg;%S=*PaaDz+g)6;`7u|=VsmVzb#^hB)D~nJdi5OQM zH@y9_A237GcCvxH0k4kh9XUtM!l7jSh@E=uxz+ju3ExV9(3EX3t-9bU@Lnrkz2rXj zU9|pr??f?y0dyO=A3nb&!>^D02D3CGv&+V#(l7-`!k(^z#!G$62YaXy*dTp|M_IaYgluVkOfUj#f)=F>VjgXt-AoBo5k~ zs6jNQO7&TXpB?sl!F7cZncgbO%X^$@cFlYbm?MQ7Ys`+jxp?=99;bpEQ4|iP?#FF-@Y)ywVUu2G`Xn7(W@?m5Qu^@g=8RqyCpLj-iPLj)l=SpL zrqNbQ6~q~S-&^tLSfv6Kz2hqgjQx%G67Jre&~436y;&cW+6$V4RS{5;vyF!YrofNb$w{VlAZ#i>bp{98MZul+W%gYc+al zq6_M@{C$hC!c(KdK&Q^|h}HG@P8bUpmkE|A<|r;s1&(dvFnLqU|Btuw2$I88Cy{wtD`k&3xsxHPrdO=^X;n0v;m4CcAH3 zGV51uCR3M@>!j1x^bf(lV+c9lri+X;e*ex^fJZ^Y31Y@Z0@gJQQ`(Nc-tVC-OZ)jb zKf@G#(-o=b(9O+L8Bntfo}GgiR0D&5 zw)w{LfHz`5{e_fJbg80%ve)TDH*;^-R|2%z0JJ?pNRhd&rDd)Se4id8`E3z04Gj%L zPJTx4)=+xMtx%oa-Q1dbojs%Fz7?zcl`RhsZ?km*v8Ci3eM3%nX%qDg!KLWxA29}3 zQ;a@oWGZ|Lg0F|zOD*GPEgyWHPpRA;crZT%O3BC>4%OStRwAtF68qa-tl2);&uM)^ zP=;k!VP{Bb!uO9vC8y?xMKWO&HZUM}qsig?<${}55Hj%ov0N5%Vp&;PshC{;HGHyL zvDfabTNxjfqrE=wltq6X5@WNCnI=03fhYg9w!Z2+EdR5Z!~|tZ<99f<;C*7sQw-+k zNX2LC)`t>g35y+%=5t&(|EWJ&$*HJ;`je2TOQ7gS z_cvYL?|!j;FkGju<_(2j>krY(?ho}C?#7mjp`(#a(%Zmf$MX-gLq+$C!bvXOa;Agc zK(C`XG0did4`6{2lgXPNm8R>~Ib3-e^M+2TykLl#{PmEzwaW6vwgB3uHf;-xFS`F3 z=A#0<-O~!rPM-sojLYG0Q2-ko`^eJxPW7tg(a9_2obDB)==r`y$_(>QwPt@(4|r2k zQ$aR65Ib;1B_;-gB#B%CM_`ybGiav*hG4IEqY@H=fU+I@Ldn)QU*G0|fzU8HBJkJS zzkt&xmV~MV?l>Xm?^qpw=M_!&Li|}&#s~UGq)?PlsUbOkg(6V{Wpa{B!3{|a*<46C zg6Ldc$M#r`fwOUQm1N_$saLwWjk=*hJBYwr9ag6fl`fEOIG3}#uW#+?7vO{oO2M3y ztJ_5G?dPEM*cymMn{ol+{rel^+0_^avs%V{2J4jNivao-e;A!Un@gVO79MoJj45<^N(PJaC&30Q9e zBWq_9Au~kb&VjiWn^UtrZEc<9oee~08|J6s@DmF-f^SK=8mB+AzTR{;4C=y`%9eg| zAo9c}Km{*lBK&hm!28I^NOS^%p0bvSsVT}=uYye}%RunQpx2mW>Vb_23mo=cOq$xO zlpGz)JI>K&W@glzJR26yA>gf-udV{=9^R{f3}btv!%4vYPgf>mG|NKCk7>u<VNS zmFuV*Db^mBqk}@-BGdd438TSR4rKB41|XnuRuT9-fFdSA%t$HGu|PD1SlJ=B0D5wyW*{F8LSZRu-S=7)H%Fq!bnXTl7JC z3p!Z72$sGgh>>cW?bC|pF(;R8`i2Ia;nJ`XSNvE)t2M<`H|km{2X1@-nh6zawqaZ{B+3JwNrJSJV? z)j+X&tP506`9~acI&*j=BvBFfXY|(ZpXSAcn|2>>XxXHfdTxPzrwm!jY7DQIS;kxT-?$y9c3z<{=}-9* zzKm|1Y1%wD_ri|^y3%?vXbqw3=lhR8cZbPY$k4S~tCp`mcOB2O z*jkkPtB=oh2J__g>83$K=W#*mRUPInwXtJ6+8ev5(Pl;ch^rP*IrmI~5?2c@9=F*J z-~5BMYBlaRYhC#6WyxW%M|dO6A4(576_vf&iK#@)nMwqnJNipxQ7dC`Kzubu0+yvGr0fB)l1Y|=oMhmr0w()zdlFv=IQvwSU zR5KrT&q5P7bX*K@CWRhBz zcvkD>@e&efV`t#j;VeE>92QB#07|h{4Dw3a#rIN4xN|c4Qe+gL#a4kp*?99q!OR0g zq5>4?!kV`F`8;uRI^*Kv(l!lJS#J6Sx*ohIXT>)30+p#RD ze@=HMgN!Z#Qkx}Pj76(pyI?`5rJ)f&{G{PP9{Hj36&3~tUVN#T#@V6iSZM=`~IlWG-f^_XFkV9Li)&Jy_~c-Fd36`R*| zg>+9bW6_7IvzXr?E4}S2pXRucD-~4u-eGAMEWg)ud}KldV%Prn`GETPA@&i;Dm2VB z%o@*y$OCEv|20(0`Em^NcypfXQ)&18BTkr_XVoX#D_iSv(ymci!5?ay3x~CMT+d#L z{PrKl?1)fK{9Z%qGr{r-*bY$gu*AJ!+`8*HWntm)_9XQvtCRkGd>uJtN4t&c$$UlZ zsdi_3J~;v2u5d!ff(M~#NN-r!IYmXZr&2{-qmSXSo-3v)PuV`&IK=J;QP8qfC-OJL74NJZFQ+Toz{QUuipFgB_(1JnvLkaPcf7NlY;H+zVGMGdLPgv z-!d|Sp~j?5-j&ibsXBSow1t*P&SmTLsX~?3lq8;s-FlD5{ip>lj>mw4QJH9FX19E| z8q3~T?rY$oyl-|TjZBa%OQ#7k+gmnA9UbkJf8x*fzvItU?U#mjxP8h05`W_T6@RWz zMjZd|;!o?pzf1>f4X>4sW{;6BlT83n-zzGCEUgSXQIiiJXOrW(8kq zPBbeAB!(mMm@8;(sEDY~89{=(X0SBT@Z|7&rJaC;@kS<;pAPXJ* zatzaKhZ;f)`R<^9#|8y*!OsBD02lx);5h&e01rR_AOes8PZ|Uiutf!M0nh;$089WD z02_bLcj|E5r7y#0w4vD0muOq07?KAfEqvppar}H&;jTHuK)}HMgS9l z8NdQy1+W3w0UQ8Mz|(PZgY8!UFMtog4|oj_00;tv0K$Mb01<#FKnx%bkN`*mqyW+Y z8GtN64j>Ou04M^K0Lp;302RPHzIzzkpxumF4nSOTm7)&LuTEx-<74{!iD0-OM!0G|QQ02hEOzzyII@BnxMyZ}#+ zQ(9>EVfRSJwn4->7*bg5c4>_({~kF1uK7lYUsQN(ezH`r`XV0lEB}|WmReu>^-Q?| z*5c#tEyIn=^;LhD`dW>j_~h7`8Cmd;lI{n~7|5^ud}TE?f1FpU#W7h=rX!#US-sJn z_hY;+7=8#QZ4XfFQq$5>a&Sb8$Fz$^gJlhn7?k1O#wR6-0c$mo?CsQK2rL3U6ZoM2 zu%N@0E#7k0!;~x04osf{vu8_jZ!wyxa&li_7>rqb0y@ zj0d#ll5htgG~P)3oLS2G`NgOzf+hhsqPFF5?C`|7-E>(v|jtxReaVccb+e53d?*Y z?L4wC4&x6~Hra*C0A&VO9&jf~Mcf(Kwx&4fC6ZcJS{D8z1=gww7hww1_TkP2$QXWB zWc_Hs3W$*ybIOX%LT6(IV@rRUsR~p45T48bcBc2Xa%1&*0#3ei-)S8p1END){>};ULe}z1v$l_`0^FpMJke( z=v`wip5qWFb4uv6or8+QN?8ZouC9jvZ_?Uf;G3wt@N!;zKJDEr<-1hF%8OBZjZXw8 zMSs-y>j}X!r~6g6kH~LfV5JJYfxd9&Pm$eO@@RYh`O5YF1?s~evhie#>c>`MG))ry zU7MHx^d(aEp`V+eGMsQm=O`VY|GxBI)5FK$t+dT-6qqvbRsX z?|iCgGdd+^7if3&@`^HAHM+HJ%*W(J`jl!gNzf^w(tzq)LIqvha zAe}K_{~;Mhl2y`T);w{Q@4Rs6WL8(9RFR)%p)xY^p?3ls#t%lsPVr0Cq6<>2A(O#r z_P}(h6%w%eSzgfb@fQGynIE5QObtV zNJRJYB^i*UmFwFx{vo7eWTtLssLv^gJVU++|9*jL$b#Q}bj0E@-`gaf{y-pZ?d6%6 zOPU+lV@c6EoC+VG=&o^2eT#O%xw7ouy-pnSI^)84MHkSxS$njV2pTFNICN8$jF-@ zYn+iGWmRn+xvM;)V0c<>8;!H{YI?ce z(KoL)A(CAPXlco)L0QKx(j1pMkIu~SQbSGe33`Jr89r@SHduA~cDkpo$_+mDpqOpkueY(AO%R0h-cbk% zc?~+GrUruvmV8}qswl$s@#1?NrxhSE0?L;qX1_o(t9DS)00^6=YaD2U_ax;6L2@9& z3F3yO7z`SaMtRTPQpgdL%jF%9?!=n)+rVysFvbRE_5U5rAYGA8?4OOi`I{1?o30u0LzyZjn5;ctb(nAKxnt|H1&%BiQXqi7NB5m_$VYKC31e5SQZ zZW@JdHO=$PSHxS8KmpPa^ocs=>G-=x#NAJXNF=K)DWM%kEmnK3@mcHENz3Bo4aY%=CT~Yo(7k9$KD_@hA0Ui-gPUtvAm${_x#)4L z^ZNeY+(h0ywBq*{rh|n#I1gx#g6knt$)1xl&0QyU7{}ac@VG> z40H|*pnzpV7PXwZdR%)m*-*+q@vHaW_52n!1sw}|CRy$ydRO;a~*xo}!WDJJ1~Q?pmDlXL#K%pN7B zobqpClYWQuGf>TZe@5HJ@(TSU6ia$Ud`e*BgCJp`Y*{ve(9%P^1SzWO)9ooK-`CfT z2?K-UhW4$BnIL5a93K<6Cy`f9^9w6b3U5@mG@Z(^SrYr>Qc|)plzxWYSMKXW~uKRGW2KMneWM8XHO4QPa0v zpkv5!6UvfI**$l-RRYJj_MVuTU+EWp)`%sL_g8z~^M!hkmcAYTxI-S*8-COUGtM#D z+_m9zG_X>fP*9Ajfq{wt6$l8=G~F<2xv))w2{P6);FoRWL*>;;8%aP76nO89%#Vhk z{CvT}!vp{5^w7*x>^5YLtPZDC_`5Tn-E=ErvlcWrxVyWXtv4>G=~LbF21=~9(L*}( z?Xh{K_yiwek6nds4Gu)X3?VV=@4m`5?P^{(h_D}E!=Fo+tfBEA6|52%&wVJKu?m+q z-rCs~0n>8Qxx^winvC;(sSYppXZ(-4ZO1t$4C*ZT?N9B=QDQ0hXb2OHxQ#-W2uInj z?t+K5BQ`ZHQJW`rabSM}m7L9rko5MX_V0@Zn_vNM)+QP#*=KD1h@G{N5VaZ$nOE3| z)0SM|sg=lR0n$qIb-@xe(o#h4<6me_ukYpCA6zx?ybV-~sjZ;aH`Wb}9epKM$&r_y z{IWnrJcn8VbPRS64CsubhaDw>x$95u^)WmMqVm8vCHSV{Fw#~~x+@YLn@CJH3CJwM zQ&XF^E>}U{6FfY88uh6WBP8ee*zRR1wyiCy=Bgm+!^OZ)8+1ZK8ChKETu4kWm=?Yj zQw^Foem6FzNR(AnRXKo8Gmevxqj}(#K{(KPNl-I5s6ZI#Z`;VMTc0*!85$a@t)_+- z`kL}}Rb5@8gbmF_!PQ%bb60s9EVZX`G((`qZwmyk9QI*7maBW8_(M&=>!rjM34 z!^7xwcs{kWe6<}ci(?}TUmhAg2^$1fYE{->k_|;BOt@jt!&Iairg{nwa8MwrL zsBDL|>A#*0I1OxZIHq5jvvLd&o+Y?R!>+7t`jnKg-t=GQXJ*2Tn$d;Ed@ttt(_8VX zySu-6A*AkV`%q|QL&!R^^d!Ol?r4g%+Yra(Pf(0l6jA@2y&S(%0lmbIkV*+i7P57? zO#HfLKIfGdUNIa1o^|DIHIcnKtrWq*c+8Re7zD@bHufb5RnyB!zUX;AKNQ%<7k( zEk}n%&##YD(d8Ew{<%VXU3%|jiLIZa49TC6snT{!RQ3j|Bl}A(MG2@qtTOh_-^Gaf zo+%$jW(MUZPED&~(oq`y>Ik7>TmLhG+WH!V>R5 zyK>du!NWrzw?;2LM55d`uRt~b2UQUmSSRg{^O{N6>#^TaBe+8Zvm`z~UTG@#Sop!a zpg1uuj!b3fTf1>nMQTAM7R~fs+zBP2#fv9nBJh-2ZcvALF!HRu|J|s2d+s5VWxw&N z={oVyLJ#{T(y-E$H;D>bc6N>}Fbk4^ljwn6{J2dmJHoH)m}ZlPwqLkEyv4!d6fgB{ zWNZmqqX1bVAEOwmqoZTG>~okFyCB|b&qvG`lh5?)EnF=U+|j%YSiK%YAFx+*z71EJ zZD*Ls$;p%Tb0hnqLd%(8#lK>nqZf6S;-QljOhLZ&B__Q;plp}uFa*II{1W>2n;loS zf{(0P-Lwt1{q2gFJsf8WITw+d}Qg589m z_iz9yB{5k6xmS0V4Cy%-qu7Xo73Y}~Oi&9Pt~AFWLJzqjJ~m$IkJWQW@LBXOwA1=| zI4L@n-qxsi@pEo8R9%CC{uO->-jCOVE+OWR=9Xbg3J?CxrgcJErPAE3>JN!blm^7! zEJ7j$>afiLe$(t5Hl^A_TFtc(q9bf>dOp2t(cWJ2D|BOAy9wAcuxzAdLIYbT~Av0WBnV%wtBAeJ*8TvhX zsG{Z*go-&7Z(ub9L_yveITXc!b37NN)_@3{Q^xEgo$UP1I7ys|4y*}w~S^nBL7tp_&0G_nC z|HFa&f3^Ro{r+?Lx1amZ+jYVIt^V;l0j}R%5Iq3*U#eRZa0~|E7&rk>4&+bzV*_v; z|8{x0|1ZHepB&1cj^W>a?mur20Q=YZFZciH-g1GjC%rE>xX({o?5F=fxuLUz?Y~{X z{-Lv5~3Imik38Tn_72q`++nxOZD95m|33@TZyFqYg3i>o=*QR` zNrWUA>0eti>Q&M)9p1qp)20#C>SLpa)UVgP{EEJ~#}o#8W8&&2Zh#Xr8&1LBRQUV) zL4dtsfqg+waj2@|xi9pyXU|-Fs48*L(T7E0;K<0*x`6sXSU3!W$lKeSgpvnzJqk$V zD0_qO3`ATk)GUN@A@VF8j)*D=5wF&_-a4{o5gTH$wyU(uoGn{&8on>V{%4Llo8sDctWZ@ zb$rW5v%ct0nK@aM^n5{d#4Y>~ot3q^<;lv*_LrDW+^d`0*0DJ( zf*FFVi#i(&H<}m{udQx!?Rs~N{HcP@i~Tt}ngitOnXrSpWVaVU0TKWo>|GVh{T223 zh&!i1gMh8Q{WI>qX4+=BDYl}Pi3yy=#YG;s1BMIYS-39o??;!XT`S>jj>JUHEa+8- z3x4a82!@XYvQ1G!5AJs^SBJ+rT&cbJeaG2jiRA(~?{UD~Y5k>}-H4=wkYBU(*^Yfb zhei9}+VLKXH=pyMEHFzrr(eJ!VeCjH66U*_A5av2 zllt!EI$|1u^Y{_`!otFp{sf$@gM+A&5<2iW30qv$)r@(Ym#ND@%kBT$JeH5~TY4~m zEUWNyV7u^SYT57d0aZ+Z0tN;~>3*a$%vOI0s($~Bs}cVC8PXo!u)RF8{88~1Ia?V! z>w*Q}#o>}-*rRQmKRQ8B?m!NKR~!`F3pg>7&`hVT-CZN_04VFJqs^&xR>q9`*2|)P z=Kj2QF+1R*VqNG;ft>2<6(qYIi~a&H81w{Q)!rJ;{kGcraY?bE$6D?Bhn|K&uHL^J zRRiu*^+B^jSTc(eA=yg?c-;eCsk$G2HiI5MvVpP7yW7FZ2mB*5BY{;h4c|aBAh7i= z!*uM6q?Sd0)OoE^o00buA@%Ma%W>lKO7{w7-0hAwK0bbN13ky0fF~IjC#QMx)teiR zyVwXim@KEP@EF+c{~Zvs=W1-ce6fa9%U6N`}+>_JOkpfEzP>m4yycJ?)^wB3e1p7uzX-d;y zBfD(NJM8=S>@K|~&*VmzO)EhV(#0htu2x(Z{Z!TLlNmv&ryO-;OjWqMcTULek`IsX ziF0Lje`jLm!tsp7w_u#X`C$Mn@v)ZR!mH9Bh$4Yna4UB|jhPH72$75nug$O)w%4L} zxpV|pmN?e%WWA@rH^N1^X|?EBedRVen{6U6H6Ud}rWI%JvCGraaWO!{!KB4Kh*wO4 z;^hvudbvIZ8+H7dXJUZ-@Z`vIEeEZn?*b@#LVBvLTf^`0z8Tt%zHPhIX?zTAm)d`q zRqD?bJLr>xbyiN-+1c5+tgp7Dgs7}6K6w>% z$U(?QH!q4Pf$)rdz_it}l;9@hUD>u<-%?xlKw>esbI}e)NZ$tB74UUj+1!MauJm<1 zi26cX^0A9){%Q$@?_+$>djpIInz`9o!+`|OtB0ngHnV1UAiMIfs5p;eiL0fYuD5_* z@@VA2`;0nDPp6x5&qjRoWHkNb2M$VKpcsWFuAy*8V(~8fyEZONXqaq|E^!uR)%#$Ya)!4H{%nQW8;K)cOniZp|Fy+f!)EJYP z;_}LNF!4;^)CkVa%`JL8w%NLp*hvJ8nf+iIhAQu*tfHecQ1IsJfx~K+BRfU$S){;!@)gX^Q9}Vk+ z$^4o1rAfA^aoD(ck-#R#RmYS5CeSuHRX4b^Q^K{rKt-aO8oxp^xKi2sWBinvj)Vvp zSHBbk#&piAT1!g;L4#~iaFDs%0q(rVsOlipNMr5+9NV{$A1rLMVo->PNK&&C;2|GU zPlt@k&>@_mP|@YliOu9t0g;>-{g2XyBvWIpWR^t(;YETY=qSaV@4O@U`OwS!=AWnZ zc{W|-)>-sz!YnZH0|QB(uP5)B;XB|Lr=s_SVcOdcy|Ldp%QGcv_C_g8WAW$hRolbts%zvrr%v6hZ=e z3aYDXn@3wC{|HwJ!EPAk-XT$b88I?7^#{K0Y+3Q3F~fG(A-miN3?Nqr)0|X50fZhj z@HUTMtt>BlgMtv-IRofO7BMlQQJFO`GJ*zT&uqUo|D&s;f8wwAf5&5)3_CPfv%Mex zCH^A%EB^Y9VZipkiND|;|Ixqxqd(aCg<*hKWmhHmz`DR8@v+W&Ufu{x^b3dupX$Iq zVCxI`2Ji#;1BL;CfFM9HAOsK!_znmIgaaY~k$@;bG~fpy1`rE~1H=Oo0EvJkKr$c& zkP1ix`~;)}G60!?EI>9O2apTM1N;Ky0}23zfT!av0^4Fh37`~E1}Fzq04f1hfNDSu zpcYUEs0TCv8Uam!Ws= zfCIoG;0SOGI02jj&H(3t3&17d3UCd$0o(%a0QZ0gz#{+xjGoV^5kEbL);CRo4CmtpZMtkC@|O%0p2+BoBX5O-ubr{9$fh4hJ>JOqt$P(h5EAwPhpMeT|{Qsc31Cys3CuH6TLp&Tz2Hz}qeWBP9$`}bKALl6lTpk>aKx({(Kb_Y$-~MYHA32dU^svLa+(Cg1&$6Txs(cH(mnTx|QwiK+u*D*gsum z|3}8y#3U&9F(^0~SR$eVPqzp=qRKE(BMaz0mbgRLZ}mkerL!W_*3og>Z^N)6^E-73 z5qkjwQa}(9#RnL zS+gr^ti4BgVtIEf5&f~Rq}2-+v~4-_M>@mMswlKhO~rO~DH8j4ShN6H#FY{7dMu%~ zKpMgkqVH^+_IPcxUv3CejP5S4*>*yea(;<(Ttr*QUMnSVHYhWf8fl4GK3h8Ql(AIU zc(VvCRuCo})rjD~ie}KWO1(OnA~6Nu44Z3H=wH)|ELsLN3#VCK{;CIcdA`P>z65VW zgbDaxzfQUDn|ayIEG#Y#YuQVEYt6NIH;`xs=H!U0z&cLwfwq1$;uzHN$;lv`P$9hE zy3KBjhk<76#jPK?;(u~)xxBFfW9wYXW{?vH{5E1tp2p8J+67#>e<4EVN@(lk{BDO9lr^;xiYu5tF;uU>k0+ z`tsE)Q86*!09}@MFN)o|tdryf5sdONknkASuP3urqoZS*O~0YVFli%(>A8`axuCzZ zqhEjICw-6e{>k}!-fLxW$kw^qb$PoiCtw==i>3}Eg(#&R-5RDv-^Y@>p}yWmVr zBB7#&I?&mtTkXnj_qM1`y{W{bq-e9fa+6R8MdX>t(cs|{#tf8_-@Asl->Ll?VBz3` zE1Z=50?Jp$P%`tUB2t@3lcj}3g)BU&4uxE!WU^NGcX#zv`bY3(1C_w4vQEF1i6&`dO7sf z^g?gns&t!ij27-`MW?1J0vnv@beW$Xx0lC)sUOWO$(A5`vjk;P7AX_M7glr9yeU}~ zUOKYe4^BQL*aGO6jtpL2k0eE9LZJ<4AlscDi${Vhm|3uwa{UD!5pl;Ux!{L+pV^vU zD7F_>$|1W4Sgzy97L{s2JP{X=AY+q@ok(BoEv1MjQQt}+0CO3syuo@poCpTun8yxP z*(txagGcvx0~_?+U>)`X(m!)i%wZA*kTXdY6_p}bVct*KeRxd4W=Fu_h9TxACMs$y z1A?D`5^cxpBljFDkL~@qzFFF^q9~ooXXFp2AI2t<^6RDM8LMUA*V2!^9#=B3Bd6}7 zIYmjXuo{^&+g$n-`CCRZ9w9zcMr9;sixC_6-VCJ(WE|QzW+eO~ERpCkgw|ra=5!o$eK7s@d!Xg}FLJ8o?3z0i5RlZ&A0f zFs`TT?xWjDo^ooR=gUXvDFcF8)HdKz2tR_mnDQfP#TJ z!*2nB@D}S=Xpm1c`YWGS1$l(at9#KI=Qai6oBwAA%V4DX>dBuz2ApK(>yOO^Aq*=* zy?P)66cb7$Y+%+Wq&dQpBig^+i^+qvbM9U$q}-v@cL7ICE}+S*QR*u(0G|8)3Yc=S zhsqk4xm}m_ZVaZM71+eYC?H}EOv$*$a1SA3#e}}CBIy^m1RY1}_0FuJRMK9*Pu}uC zg(o;`Srr%`b%C(_p(F!QJWOq(sQQy!dUm$ou3zGOf>c5{7)QS=);Ct`xQ}V_XvKwm zLty&?$A#q0M$Zz((7sd@mNdkeBjFno-Y8~gM~C>T3A#%-3=8P%i;IgJj-)Xb%K^p9LTce)W5CMT}j=PG}smDf+B zbYrf{lzq1~%imG@mF3l`?<3V{ly8Fpfr2g=q^@sT1 z`QKDc~2+j`^H($!Tf+E%@*EBM(%m6lBGS^`UEf&y?0uf|-TUox z&;6Fqj|mGN=Ug@C82|D9-**NJTxn>{u)$7pg&dZV59S;Ezfdx(IW{4dV(GlhE0p~7 zUZf1)U?o9-O%Nkc(if;+oSa;izfX;->FHs#NQ}{kF$1pykC4yW8=ZIUuf~jcic*$ooFra;z|pWG@ND+9m-R$k z(!D_xT@>}iJvzCNdg_D#K2 zG@BY7Ao~N4Uh~D+k-Gn}x)HiOZ3en=>_VJG90N^gVmLmG&hNJz${g90rJ#$ESV;ye8F@ctzDwTll`7p&^KJ zTI^3t|7BEqGA17%AF?id)Yrj^i~Sn6&C5fo6)jlM09f(V6OY1KxU=f(;E~?A>(Tj{ zjJgh9waD9{$7#>z>x&d7c4k>?9hRSEI-v+{+Tt10zd#&Q=x9%d@#4HY9Nc}ctp2mA z^lIU;qHXCRU*eQ9BIJj6(zjv$v`zg&wfo{n#U04{CN`l+BmxNaprx)6-@fvQhH%LH zA|Vf$GOVO@R??XawBL{2HUX8+n-VRF1lW&GBK2=S61+%Y>j~TDUf$kBp;V>38a)XP z4n}G`UvT?0uyIO_!&*UM`M0Z)9nueyTh%h@`>FrM>p=G1=oCtE$* z0uIg-m;q;$o75Z7{}QR#G+4aKJRZ*JuffOg2`fTt&-vFA4 z<=<5j1IMwtZ4wCh(BP@BbLr_AVWIed4_z1>G;EaYS3M;x(^o~$4+m&&w{DZs138ux z%l3ChSGTv5(bOx4L9|YmOyNUP2hW}YHI&pu&-Rv0?LxL%cvVmvjVD>ETJ%eN_|(GO~C`*q=WAd@+;&tlK=g zp0x2-o2LwkT#CLP8K3)7SqA5pKGOsqgE|vFPZfm_ykX)~mAO)xks`9xAEGjHLCr_- z?N1&*-5-`;iza>eN~75EaDN+pTdv#P*SESh5G#KxMLaSrsO}|UD@!(a{Yu1u2<4`)UZKbaG)QP+MXb@xERg#(SvBm>iLfMH@Tb=9KGM{ zAWw+;(33(x`5ZUAWO89Tyl~;AypmFPfkFn&Wky4Y2(eHWN_eGMnWUygO3l_z?So@^ z;j3k!r@BiG*lHPvCJH@rrw-6851*t&-Baa6n*!BiuwIkPF&;Ov!zFYXpqui`rZLww z?TQ@=|IKDj$el4AVM`-2hKvtr3baus#Kgc75fPDumw}=$i)KkKj&bRaZ;Ao%!C`OV;}rAyM@I6; zA=hjI!fC?CKmKTL_1mAHpF;yT!O4HSUw-ZQ-Ul$c%y7@ z(@;J2BMTGh|Mc~iK=UkvW+V|(Pg^Nhzu2mOuv#A};NQ_sF7z?1xbWLDaJ>V$wfyV{ zYGjO}EE~`dNW!MEEl!khFes&vgpbVA7)nUMn0vMf-R(`i?*TXPTVt$)qyo?`yoxHGqoVqof|cA~$Lt9D@O_PoSw8!IIy z9Os%dAu?O6^Hyi6AV;eAUa=jtQtCE3eU^=V2J%QBmFo+l|AgihQ-G)k5DYnHft<7; ziyBe;WiJp6)tjlhl|)Z+@=fyd@<0hELjY|Y*M^r03?W?XfBZ9gfZE*s$4Ta|h`tJ+ zM;Nzw=_l5{PoaT>|2f`$yF{K6-H)qfY~k9@)Z``@j< zBV+%w{{9|1{_1;lhx0?gh35+-)X?vFVn&#>o;Ud+RZ!=LA7#($bj?l}|ues>O8{0zx3d8VaM_ zWsa>)|GZC`K~q#>YDi@?CsQXrM=dc<69Zw!;Vl_gQrKg{a@EvFNGA-kUoQwKT2Ngc z@X8PUMIoH}9gYJdDKmVs>QvmSXu+~CbcJXLMys#HqG>kh;C5k)bgQe-6#t3klZqEE z&6PVC@xg+x!a1CY*VqMuhP&jInjpk0boilOGFJL!3gq>(Sus(M?cM7E)1dk_Qsp?r-yB}- z15!B?N3`uNDVJlj)1N2E^ZNTo#Aky|<&_kqFM4fqbMwUOWP?+i$xO^lY2F)?EJLhD zeo{SWBfxmH6~Cq>4w_Vg4U<#e*hxfVIT&AxO4IW2z>-2BueJs_|W98g=B1YuAK zbg*;*#t?L|1KrR0R##VFfkuIuct&9B83PoI$-$t&!yZWZ*Af5tp$Hu}TiZsE3HX6X zwz6w;3kzgZQ`0dkkru=ok|Qvk1Xc+mb*y3_hVH|1yYCZDAE3j^1UP z?YN9%Nk&=e+6-D+gx~I1-=G1Lt6;l_2aLLUZ4M+g(1>?&Lns2BjBrnzADs?oeApdf z^E|IBx4-K?7I2+}>kzb(nTvd{n?ea(;N7O~kzYTSbS`+fHaNSlIth&1=fUi|{^or5 zL;}c>Z*Fd{UUt0dqi+~H%0Mg|CVzVXtN>)dd_!^ZS;z0V?H84wdLgMQj0t9&V98hg zw6RU-lKjWuk1V^NU1w7PiG{?~OLmM&FbWC^OwXRlO&2kHR$tP_d$mqYO;t2E7Xbl0 zh+EHJ`y+-gwPCJLPi{>>MC3Ree)(za*g@tsLg)I*82wHK!$*uN@08&P1Ux~u_z?vx zG&JMWMaBltYpx5gss!urjwh6iFRMxeQV&Kl>k5eM7+WyXRFF;`xp&xq8daEMzpQ+z zCi@fzkHQ~}DJrXd$0jEHm@o#^vrIyrwyJds`@5FEQCuHxNGXUqB$2Tx~@2*!$Pf@Y<|)$yTb}QkQm66mGj#MOf!grIn zEdq2Ud0r%pCJr3=!=yOyHSC69wO*Z|+00h;ltwkn4bi%pc?C^iDQ1Pcsuq3}__;g@ z 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≀rFYDhTj*>%>!P;t?jQaK7K~(hL%y6 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�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

hmiGai}M=Zo(o^|1PK}VH{QMZlkZOL0T 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

HJ$VEp6$Ck( z8AqV=*8Xg~s#r!Y?P(of<6oPcTJPHJ%*2gJWBtTmnZP6t^DA%5L;>iWYSWx&7S|nx zLLQ*K-`>9?c0BD>CYL0cLmbH?@6#Pea4vy6HSpU6taa(w9q;7OE)LY*R|JRN0!#dJ_etSL_73scp>oXFsT<9s3OIuXD5z448 zX*=3&jdQ)PQo%ySVyu1W7fYnH`%QdaceKKyGjBnQsqI}^Qpw$qnb1%w3;a7gvm*5PMA$IRkNBd@@6v)5bo{6KlMijI+RBn-h# zI*-#yNonQ>DKO8&_EPzN@6FPb4QI2Q0XtfZorDheZ>%rQ>ux5it43Zh{O5Oxa<#s) z&Cw&a{i2R%_Lm-|_n5--8MaL1ev-VqO!sr&^j4yOSmUlA&TJ_uG;?>;wi4FUMK!@w z5Gcv|RM9|#8%p?xB)c0_|DGLpi+hWIvhv|@gjdk0a z=1pwAxUG>kVLA9VoIq~%T}^G^R}p1ssTh(wK5+Q` zg6{8A!|?P?l;lfVRb>3Knc;sMQW?@D#OW9f12;Kn6{{b&(f2wPpTw1(Z%}T;tMo9w zG_)OpGRNBC>ptJaVE%)rZI540EMDU{n`Sv~hi+xVL?%a?Jv5_iT@Y$fEObMuL33n! zI#E3htDZ&l%L|=^tzMUFe}8?yQbCRMxk(uf5lx@<swELWW@mvm|%qw1)&l;}P|3Hfd_bA|W2_!;hKfaIrj^Hak9} z8u+hX-u>$Hg|EWitz>k`Zs9BOD8GE^oDmw=oEs}jfd+;8wr@2w5_NN-R_QOEMGATB z(0T~A{p58>H!FQhC}km-*|8T67n-=AhRzQ5{p3B#WB>kWY*;@p(oT#fvB~@rtcEUb z6M8jQQP~Y3^zfZufbS|QHez4E;|FAANFFG&n*L5z5hqM_BG>vdUxcQ7cHw-OWh6>Tl(=j+ST zbxO9hHka)-bU&u4Ol+62v+t0JU+XF#a6Yp)4YhO3{I4S%nH){=#x)uvfSt|;>pfzL zO{k(ZPW6$VE<$_puD_nGr>||FXTq#zB|SkUffE*{1LnnU^TB__7w?!kQ!o4*1m`8M zj-lj&1UecacNnEU4VbL)&AU#{*t;+sUdqP47zx7%3=3mhMMhu9i$Ig5?s1w!o8 z(mQVg$sPU{{wQshhoWy+YDiTh8Zn5b5bdu>>PVPgDkPDH^eWa`@4~`s=37>z5V*k+#_NCU?`@*t{>Bom)^O ztR70V6Y1>n_lDp39nnK`vv|Qm8~9}zHDKvI#Wr;=Hz*phK;C}2mpq5H-JT;)CKn*G|Oj+*Q6RJ>^K?x{qncIPzcm=ILKJ&)J zrQ6LxeP-GVv;WabFOVLehy^eOjEPL(R0B=+Cq@OdY;gr%HbqbP2~`=t$2RUpQD6FA z)bWDZKC!j#`*KXKE|w3E46$V&G>z=A3f^>zu7~|aoCs@CYy6vboU`o_SVUIolly{= zRc9$J%=2Ux%-J<%(zw?dix0->>O73c#PA%wJZm|RqU+w*EAS?>!1>TFDv`&sxUlf0 zMVpy`GB2=OjUwgt{(c5E7|P!_&SVP&3qvg`V4n%t%Nu|cBvKrjbOdtWr(IB6z3NdX z#-I_zZ`}Cpe_#6Do3_G!nCsuC^F?MDBkH=Ri2hdDsfR8v1}FxU6cu~T&CN%3aU)*Y zst&lhxj$_0?6Ba|nbX8%4>%vSDi(gx0SK#K?^v%D=?kQTdT=rg$=0b#3_m02$0$&7 za3pXqC zk?oYQKa)5tAD~Nb6C~`|4<>O7VG>76*Ec(}5@2a?`6=cfxQ>`^%9yMHLb%LZ<@`61 zPI3b$k@aiPpr&yhV9{fYv3-U1sG1Bl9c5`-Be@^lYF7P?oOJI`ix^|8U_EQE3lk16 z7BOjWN=KQp%n~Y|m$keV$l|0hQ1atM-$AI)2HzGx7NUoZqjL^?2j)R9A^cfJZ>ozW%fb4s+G1MZ3ebP(-R;JtOWFrk1E(>$14C06BuU*KF+D|Uhu z;*cHUN z#951weo#TXb_7%_DcXg2ND|haApsPteFARwe304ETO;qUc8A}+-aoxny_sp#wh^NS+XE1StAi}>%xj&{E|Fl^T2gCR0YT;^ zFb)PJ+sVHHcdL(XoB0i6KUXpmQ}57#YMGLT3IdyqiAXwYRE2&QsXvn8Sd4MQ%0TUB z{-v#j!b;z?hfs|FPMUE?ddI7K+i3bB59*G#B0_`uVQF|@+dthmfp^|aTLk?9OoA)n->17ip>>zA6^)8zZ>HsKfG6ZM2S1S|jpQ{yBhTfr zoTlA9znCWrnbjiW-5|7;Po)w+J@m{^9|u-M(~i2UrVy9bm)CJF;99MeMr9|s0(7~&g%6z? z54Pq(qdyBm7m_Qbn@_@gv4MVCqy*(`+KnU7FjUFWquUyy&BQ)iYB*?K9FoCh`uMQp z{r2r<(LY-FG4P5jdMv)~ex&V?b}4uQ$>;<-1XS!I-ilEZ@Vn6zMu{WksNsIV=r78Y zHNwdy6VNjJ`JLVS+h-a~0_2})hkyV2t*&adS=4gk*7AXUgIfVHQ#q4k|g8)Bpw0YZ6IC15EXk#JAbn z*?jlJwLAa?$bCcj?2D+_y}qvdINYKd)WLNX$jHmV%PbrpWBa&mOx8wM6K~U)&(n}w zPyhofAj2-~uSBkru2`a6vs8)z$0kU195u&KR0H{s-Cdb>1Wb5 zC#JNwrzijG)E82g=q)E+3B+}uCfNAXPJY-*VxZ1KGy;}eKZf0b$)F9Ip{5)HM5SBq z8_y)y_ym@AnnGTI>XOpz={ut#>6V;7my>CebV*%(kNGHJk=7cVvvvdVbQ|5ER++H` zkNUDJDk6cMF8x{)ko;?f5gn?u12Y))*!(t`|B{7XAX%8Q|J_$!+DDxoLKtTY85oPy z2xr1LUPn{&Q7Gu>xPNzNAo>eG*894r`%>N!1e@$apOCEe0KS2nq2Zm4!)6GPznIGw z^<8H);oj0mO|hLDOs!^E=XiAcU(sDlhK+URvv{w0@OnAQY!NR6AST`fYS^YS;1!V}fOJYOCqz?a+l` z0I2z?c{8cwVK1TC92E2hw1cxu3yk@PfKEXIPJ#k!OTbdBD=`opJsj_MIJx@k{wokF z@j9hZ;&K{!zV#iPOx=JujW`-ONrB)fC)X~*at6@Ef)kN7C|xT<#f0S8GGZfedqbL1 zT^R}Bd%6h?K9Z-yh(S{fsNe9irk7z$2jXA@IdT5>KO;3)^+y}!1&ULq*UXrg!3Z{JaZ=*0kY1t-oYO9?k3gbYZXA>zuuuzWx zk6uWwt;)H9oR+^RfjPkz%y)F>X16C(%fOqL?{Cl0#yw4eE4-W*AJqpkl}^A4!_n$54{L7?RNX`a;DQ zin>jbRG5guhq%&#UI>zA4HLwluo0=zAvNi+nvFy>!05;!BIsdIh`qZW!cFRDh26>_ z9+Dr4vsHw7=zhuU>Dr;_`YH<{`9T&49wyaj(P&eo(2=2r!>HCc>K9elywRq4DRz4# zn}e_{i`pI^fVrt}*MWrgvgc!3pE+aDx)Tcl7TCCaMjHxINT@n*f=cZOcTkKAWULQC zm)lr=m*w^J!SIBzxT`p&7DpZVj-K=Pn|RFF%P{_C*aLk6rI+@`w>}t7F0rq6UXNwp zQ{V6QH)`eiZUY|O92+|P=ADnCgNRwf@Kf4&B7EeNP9(B$x+5CW0O}W5- zM{9MWI%fk@ci(!(kpEzrJ|BitowtZ%|7QQYqO{mAH;y7Cnh^6n?ueAT>Tz!DwugJ+ zt5f^pePFCm5^-898)STBT1BVL*@p(}4KJ%Tq~kLjnM>R>o|k>^_rj;iE>R?45J!}7 z!5SN5C)j~;u}&Lp9%XiC*E{*tF32(*`74{>4>(-pp!%edU45Ox-;wxgUcN#;>j%T= zINg&iwaWC4SUZsvg0YjN7iEJV#di0|S$SL~`Hyx*FvD`OANa_H#<#G-0=CF-e#P5w z22=?#Vz{f*;zY>*&h4p9PUn?H{)T44PbQHGzkxL&H;(a3l1kSj6Y-lOy<|>Ljt~{@ zV~{RsCR$~5I%Np2pnq33=7i_WF&4s9g)Tm7U;#%0$I(5%8z~h%<|VrtphK+`@Y=J$F`-7Wr~Wt3ZsM&s@x5&bpXTi1SI6FZzF}-jD7=qDXAY(Xvl$#4|&2Q zxBO2*iKv0cc7hEy-?!kJ@kZJw?hfI@KzB2Bf&{t+;?EziEiCo8W3>n5F9+=E2o`u;dJ!EZ`^? zH5p^qzL~S8Z6>Jc`fj}5Ia|pYbyg4@9UTE9*(SZ^U=l7H5H6#7XCe(J?bmdlbFcfB z{~Y0Q2pP&2#7UOUqK=bA)GxO#4*q*Rw~6*JMqMML{2>+D^{W>uU_M~Mahn`x6v}F! z%k!<^e0l^1(>LYNN1#qRBQ#~5RQsCD9`%NBwb~x8Mx2$Fc3^UHQBSwS>GcF?OzS!M zmp<0)e?km^GAWTP&83shOiC)klM=g{Z=M&6@ztQiZ4M;YSN&rBWuwyTtxR@5pJ*#E zmN35Cr)zOT&}AR4>aM9yvp~e0r139MaOs+v5I`YG`;vyAM0XL?s-Uc%4?_SA@Yb{E zwO0_29C+G3B79sw7rgdtydLLO(hZuE7Q#_kA7Cs)=n3q!$3XZ+4^8g69_gjGFg?p24mRF2iiB8i^3)?G;)ah(Bw2OW(xmHpLp*MV= zxpa7JxDTIQ&v{vA<1zTW6YsgBQ%_A8ncTEagIH>rY9b7W;V_%l<+v7=FApyGNF5lO z08x0{w0l0AO)Kf+>+xgIG@polIJJK~)M;^=LQSIFy?-_@xBmH~5D=rJJhi#sPxH6& zI{@;HAD`1w-dhBh#sZ9v8gAGIfUkeMwsT78p0;hf-~93IsO2dtFEL_3@{X6i_h2?> z@{nUdnr5oV?sq|ZeWK6=S-9%)Uz~d21Ngg4#6Z6xPAn9cj95YzhKTPTmoP>~MpXqv za*mE1tqPr zyuRKj$iDgh&tC5TTgJx!tKr@M|NMVE0}e8vh2@wpq)d^^qdQ^YsT+)Do~|3n{T9b5 zu&2vQS9PxO2`RF47yt)wEzqPa)+eSqE2Oz~4bV zhfwUE$erMbyB=sCeFyM=c}`Wcfb#P6Q`y+%%KU!=IExku5BVNdj z04)ttiJC*~S5#MRh82(OhW43?1rTfE+sp1j@V3 zzb`HOEq%6L=YiYp?{4$s`%9RKA6O}vDuCjW#N)^a6t9lL{99CmKN$pWZ9K;^{9r() zFoj<+&joqgZqJ{En`SU@$diyMO;XmaDEvW;k{QC!w}c(`2pv^f8yX%aLmh^I&}Yg? z)9X!U61AUMoio-E?Q-Y~JYBkfmmFIgon_!HCI)D?4VLMf$7AMyUuH|Mym{&tu35M3 z*Ct6A&dAww=Ck-dKsJH*v>Wg|42d=G23ellowxiMi5;dpMdjGJ0n&^mM*}n(H&cyt zH81#I*3f{(niX#6|IuQtACw9o#6d2KR@j$(K;!UP>HBCPrzJI6w^r~xJy;Wp5ZX%g z&lNCYRlbxAHz0#-m@-*xv$WXq!}F@0@))xA!$X$gq+b`3CWMk>^3l5Y`zs~^@y>@R zeez&jeSD2%kQo?F;34}V=ep~L4ax7T4at7CP+;Nq&QMUpB2c&NWlvgsVNB?=9(By) zCxcf{D2A^Zo?$-@c3;4{={Eaz2#GCRQ_;E{NSomEhVN@k4JbPejS#I9*sT=h_(ySQT`-FGi z8pJ&J*3;!Rw-f9C0H#OZ8kQtbyaDyzUV4RAgX*@xojwGSMc{ipvrP-=^eac5lJ^0B zoHz5_o8|16^%q82sO*A)FT9aYKF8Y`#WjrMp%#*5ewwnIeb*C$f~jDAKM%IfzwaLB&L2z6Nb|BiDGYBmTVDKh$@fKdwAY(n34N4mQ8-HI;1%|S4^s%hrpov3FR0P zJtJ^fiirDZ_hzk$14Fks{buANdhsweBR#z@UP%DChYpApGN;E8`#YY`C1mc3!qEpN zT6h?*?FcJM=&w9SRF@$=@Ik}T%~1-2H9`R^Fo#q(88N}TWi1bZju2r5do3X2&11-i z3r7fo(0@n&tcU)4{Yb|r2HIejzYOObrFNNsH~|-yV+C$G0JU|?^z^v&7lOZwn6r7X zB)hmDP4ND%e@ZMLc{#G6D~HcC4B)!H7vJ!fLY@7e`^U#Nq)tiR`dwq85VH*F?mvv! z)=@`-CZs+ZetslPCacdK0KiI^mN>#|nBWu3(`P1L*iMnqvlr`F@I}$}RWcEqi)KpEomeHVy)3wyiSiaggoKz2lMe^c0$<9?n4g)?=M!vzC zg&{T-#O!7){6n0SMRRe5?bKbdO&OBm@#1;9|qA8MlwJAid=yEerE5 zKNy4DkRzsfA4Y2NVut+?7Mub|GISEpn{OIt@WMVH&rmp5W3Z*3cxv2UD zA=FfN7$=6rK{^ns5rBl{*YS7;FDC(5&&P`4C90%$svU5`{R}&)2QMuxm>!J?`Lquo z#O{`PxAF+&d)dl*^ny(M-JolIP9H+Z6*#`d<_QeEQmbQ%#g`YkA75gh` z9{27XNE#3Hu#tp==A&Y><^n=ae(dZzShlQ8_@O%W!+{UPneiLcDMg3Pj^!&c>ZxpB z3#Dm}6wj15;f6o4=L+5pYeYbM|d1A@s#FXe`+E=BdaCFK11E5Ucs1Shkx zAb!IZ)$RGTfnlY=8gJkOZohV;jXv7DN76bM6bCDbPg=dI_6M`0>4J5fX!f;g@L!Di z^!r-ViAxHi-%X6ozQa+5$l*?-X`J%76O)PQs`08H zEpak{5n!kHD0h|_EdfFIRo^*&E@CuL(sv;_TF)<}p0zha=360f=t!YwE}+_e*KG)! zPp84x9u@U;G_C`@Qi2OW`X*+d8?q^JS`XeO+(Fw-n)0B#cO6#IhxG9~y*7@fj_-xi zt8n+&)Bvd3lB7vt!_&Gqd!(+r7?+U?Dt`EnCfVVLBwRG~g2G>YygbWF#$;)q_^^i>WInYi z_uIg)ZaZb5gW#osPyrTTpIS$|fXoessNcoM%9ETz;bPPPtCZBA>vB)8>Ui08gIfL4 zq|mi4Ae|a?W0ZhIsd+N$8Yna9R;awk|M{yTdt?YIheN-Ty%x5+x-m}PFx6d#`(4*O z-l-(dKXhU=hAN)IE z=4lt*1w{5_>?q%U5CBE(qlohNx@mU3L~6aK4+i=PR(nvE(&dEYMyEvpx|(x|bvd>b z7*y8*Md;gz_Z1RT5_KCI8mqA{P8uRBqZFdVrsfCNJ=?VPr6NL6MR-rrB}4Yugxp;5 z_J6X?q*gN{%7+?}oEBjiw~%;vugRe0=L^>t0@DQVM zOA?MWW9ksw{(}D~IHh%L_WQC;a0)4+7_QT<#jbcV_hxI&j^6;6-`+w$oP;XH-d6RN z&jMAgVuRB#!s-CrLE;KjHgxj@<-FnP+)$T0<<+>h+AQ?nF$;4iKposoQ?Yxf%G&c$ z{PZNKy4p1~0Kg1F7&_F+`xB&aY1Z1@8`SH!d;)3|wJ#Z}6UmU03~u|Kbw@bwQ)-bq z+h+2!bXq{wuoUJ7|dJZ#aBB8$-bSk&*29Pg6!K9iBjQtOVfjd0{ ziS9)9g2>u?5blm|q=NCa^Jg47Sx)ubLH=k?C*>0ZAbh9vXFQWHOY zCv9Wxc>YQFSz(f$tG{jLV9RD#MAs(0i+eht68eaWKOgH$#tp}+{4%Rz^tJYxt4G9m z{*4CcX^ty$77>SS!i@dZNqHY#Y}l8zpSV;wP{mrTnJ9;Gpk|yS&4iJX3Hxv0Z)6L> z?tp54^Z;B2io@b5qN{Ppwc~f29*HJHyrq_LJdsd6Q;B4Okve-t#^I_ACJa?j#!nVtk+pXVqylx)nB}h_+|Mif) z-T4;@l$nZ$`GR2tPX5M95DKADLCM#yB*8kH-uFJLKiO(vdRe5P=kn(+0n!GQ!ceYY z41<@yNAjg3OqBhLxzVJ8y6Hv$ZH#VQ#A4tKir1CM{YKz{C>=P{!=R;YFD zC1Kd7yGYU=QW(a_ihu|;A^SR)lPH9M=DEgUz4wR zFS$E?z94js77TP3I1gkMX&$0n2o6|6xq_i(88>eEw1U0aJuW?YtEi4gmM9b)SfiNS zEquD`{PLbMu73fa<*mBWH65=0+A2AVuga<4N19k~qCy58fHv-^Vvg8poTNkJ%NFu{ zjtJU@FyjpP@lvRPj&*=fPc0&)Mn~a^jXh4Tl!7Z7nJ zf!XR>VPAPqYmF*p$YPv}oAcASUh)IkYf{mUTHl`j!=GRQs1t1e#k}AKI20@Z^WrnW zpWp@_jQ|P<%l}wE`VW8OKm3IM@fZJ>1K|LF!kF#9U--}ZY{2T^{~};;1MB{yf&3$J z{Cg+tzg_|z5PZ#=>MkB9#sANbE_|G&Hb|KkPzFaF^F z&HBgjHD#jhf1s1Y5z*)oM8dW^t1UJJEMBb-SAyUAYpEuVGv%{!XFcvdqW$_5=)=5Q z-JdBmcp2e|v#!(Zu7+j1k0T-pGlYl`B+u6Y5Y9*d5|5{{u*bemDlz6LaQCW1dUTQ! zR8o}$8<6x8sQghQ%yFLmeRvI?OIE$FKvEzqK!aSjpWvVJfq;r@+s<}uJ*{7fYm?vh1n#9FpcBOn$pe-IHAyDqqic|TCA5cL3$&jF_G8RZPKlw~e zgW&;FwReFfwlLtHl4nyWn?kWQX5m}1mG!bq%Ni#iel8JU@@^{loWi(o2gI+mUwXj( zT_@l2)VzPf{KH`&Pf#nh*KH;eo?uQx`Y?kaG(`P9nHSm7eQ?s^zV-mX9xcDqz~&c& zll&ErDo;J^j>KW>-JXAo{hb1Z^|FIzK*&ACQ2(k+UBHd27{LRrsuE57$as$8))1&Q zEFQBZsNt3`NM0yZF$+aK1%v4PTI&ZT+^bB+2yBy3x1N|7)vL&s=sUFe{>!v}!7aLx z)0x8cdI?y(r_(bj*0bIA+}Lp+u$LFpuD*=xZA-CUb&u@VgY1O&`};P^HZk~%D1jYG z*)yZP$5FGvk*UA28Y7r#8{H-%?Ds;i4_uN}A`|VrFu&I`SKv|GLuDH3nW=J)48|e< zJj|hc_E)Svgjyt@mRmrPyqhdD=%_IwzFMjVbWT1_`I7<}W~d0j6N!^9M+xEOT8ONJ z3Z>CT`-w*;N1HIzIsyF0B+$2E3_^N9q*DR-dPsHS>AXOkWps{ihThC-_W@ml9veO{ ze%NT;lt~AKj;;K~JUn1f@TbBgpsIn(8wR_*_K?0Q)FTXY+iVIg{iKS>jV9sexqcIK zl!AkCq=ZgDn0HFjwv+m^5Xb^kxJ^j`K_*${As)Xpl^OB;##tDlMXRN|-p zzw;Y|n2X`7&JKwabI9bOK-mCfh_PC$Fk}|go`TMT#0dR(7>60o>#>8v`7rRZ(z7JW z-|(W2x!Z(il4$9Q_dmp(IS_>F0ba^O^W>|{LcH4y@oo|Ksr}AgEis&@f#x8kqOXxp zx5r5*#C|@>YTrE_Z#tpSBrQ9Mm@v~~*Yl!yq+xwv(MX7XZIi=O{VmO7q!K>-;b(03 z0&pzly^g}5LY{{CZNTK~q)=kDZqU#9fretKH&feosHN0j>HfF&t~46zcL9$r8j)p2 z4N53XDv4|nlXduO3Xw@!l5LR0OqL9yNyBK27)vHClj`3@WE&=B#*%BvV03MjWXs+d zS;qaj=iYNa+z z?^G*NwJ7}cNHrhFUVIGANKIrnsp$K$eNP+Mej&QC>&#-hM z)uXc4?QKDFK9G_X>)SiXaP5Pp$w^?8S;Qae;G>?WYy)fWreDc8Ar38WU%aiz{iQB7 zQN9WeT+N&4>w2vfA}ewjUJ<1z2e!5LzL+bVZ427FG%PkSz`4YpuUmfP zllW04tzGq)%Mc;%$VGAho61cr^c2{)aRzn;Qpon!Zwf&eE zy!{?hpS%sns-)3inyaZ2F43&Q9nL{59-|ch_|WWNdzE>e@ln;Po8DW2z!m3ww!4B5 z4XG(9CeYb}h_g2i9mBuL*w$GWrk0yc+D!%=ztgyt@DR}=Cnp4YRg1nStU(*=adG)( z*}Uy;kT`Vy(oCX=_t8O){;RYFYg*?D#fm6D=&H$Wn9{s=zI24FfavtV1b2Pu^iRY{K zC-|-rP-HUM0f{VbeGnn><#Qtg6l$c>)YR1PJw2g0{FF}D(9lTM8d(f5#eZjO+noVa+S~Zd3lzQM#QW7|}R$qV18~>)L(yD_o|^>Ysh6jOOqb4(UZl z53hNBQNvoP!C(vhJO(G^?W313iQzS;enJ}@C02_tlvW!mf}kadUw-c~V5Qcnu}q~> zY`Ix=b>`@$!>eaXa<}?76+oPW|vptKI{!>!uj?ed$d*%rk{Z&*P4rIge|~Rl4sk zm1MhHV|=gPLdzs4kwwt>_}TQK4`OKi;%9w*!_(6c{mT=&P_$)WM`J3H%EA<*q>>*5 zxJcCZp5|)$1Ss6HzNG^{gt#0xdQeFu%&uiBZedYc<)}&j&ZmyjYZg$*0}JX!2X?uy zbkNL-Xy5l-U*2;v`M2xD+i7x{y6S$cG&aFf$wT?}B+(*2^)vjB=?_8ZdR>~orgjzN z+h8c9Q0Z~;2L4x`cIE?EI%>~`%E2cd^PfIDm1IFQnI)>uZ-_d=X76hZp_$q7vX%FA zoA^(I$v^A7dJ;Ypk~4)QlqKfLYYi!*&d@ujtoi5i*BcpMp@r~7#MOoq_*ltF_@orv z;)3HLfyc7UL-&{0eodUUzfqdVD5&zugWWRPuPT4GVE*HEcdw8=9EVNwgq1oNJ=S+V|?hq-Fz~Eq=Pt zW z8s41lNbmqKd)Djn>zTTqocuSzoWbF<>jT#4CD$=Kdg03_GT@skGGJ{Ez6Vn9aSUN=eE*$#JNJFJAR=WOnF+*jCG=Y4Pd6Hi7t zb|_}d8o{BvC@Frwp|2hDAPS5wvg#?e;SQ zVCtFF%eyCfreX01bOus)p9 zBNyYX0&_8>w8_k{{mZU}J&PSc`X-p>x#69`9MkVkW8#99$Me18qMOwW5$X=glUh1O zE0EO(nw^}J4^GYQF)|s(|E!7qwo9ZJ7tYN;;l0+zD~LWIme^Wng(*I9UC;$JEUQ8Gz~7+wN(^&_otJv}T_z)?6!A{;JC5cMxx zWAI#+Xz7#u|wDKKVXYawNGxg22zT5YChgmL9Se;j65*f`K%g{R?-Md1)|coEFu zXFV5*L>eYB$K#@+vbE;?{QOD*7jFI_e|b3)1CH+pp!HP_wUT^d%f zMC|SDdwP1dx3^Uwi?b8&GI6%|!eQ^U*4dwhJHnwn~1VWF$5yS%&%0)go0 z=vrD@3JMCs!ou9$-LI~$fI#5k;h~$GTV!P9_wV0xb8}HpQ2ysO{(p+k>HdJgpx}_u zFmQN8WK?uaY+QUoVp4KSYFc_mW>$7iZeD&tVNr2OX<2y%q_V2IrnauWp|PpCrM0cS zqqD2Kr?;iXvP@7?{w)ZRk4*(nzF{n1LF9?XupaZSV9|*&sl+6Ow6-W(5;&WJSK7mug18joekG?Ywc@;aSuj5d@`=ZXX&k;pcd%@%?Z8Fa@Q z%jZj#3uUuqy_zZ(AsP)A;ZWLaQt?4{2#3yb~)lNE3aMQ_5t?>`X>}XfY^;G*~y=;&h^>%&G2l8j(=qK|1_&D0*XwWm|aev`@$W|0T z;5@R{+}!sq(e-p^-B;So$J^~{12tX!Yq!t!{m@up?l%k`Z?VVC^A9U8KtQ)D5DP}1 z5_k&_qlB*tuaAOpyRC=#a;y$t3M~a;!mvnxrBDSLzq8{!3Fren?olfiQW0Wp9DXhaAY#UQ{k?}X440LRid+uOaR;D|#x90xS}k@!uKHA#PunWgrw$)3X@3ywwOKxu7#{pglk8u= zNRfhxV*yPKT(CDv1!&k)6qHw5f^%K`91FBD1*B??lSH`BX!yMypUNre5v;6!FyFeE zz~c}nl=yoh&6W5&Aw2Ksg=SFDlS>&r>m)=>ScwHSSkW6mydIiCNy#~9l^Q{}gy&>& zYns5qPArT7zp4vGjKsC4v}O4A}?=+!!Ysh3$~G2EZ9Oa{PnYQ z=;uE`k=P&$jI>JYdsWGht6 z1whk+$}6{Z!x!_(M1YbAi3_}b*ya6-fw)3V4Iq!P*BGlp_FOM_OV)55MeWH2oAcPG zVaxNJyVBD8irIZ97&#`h0}~!95^7c0RXrSMURkui>R|SKn8v{^Hj?Fc;c=Yj$KrWX z6l>>sT9&JLcP8FK+IU{s!{T+(IAHgB(NZIP|3~HTx#Im*_vh``>t5_{-Z#*%HJvvT zwA*hBiXA+pojn*jwV#q0VZYt)i!jv>Vh_wWt>}yY_I*Cdt?_-isJZfez3%zu_ja-` z^zSbbUyw*MXlF zBD?@;o^F`C+tMstikO^`$3dKnTRO~cVxM@_LIVBeCbr?mIN=`%D&|?s6Sz=F@#r4M z$Z{JW2Oml)`ejsBYc5K-vxtU=K3XPcATbN2m`)OA%rVI^sZ8D!_lu+KulC#ImQgAu zbD43w(fX9uiQ+FZzstwQO4#qy^_)-L7(Cz^(Hyt88pt}3FTHawpK=Yw%64AyFBLdOB=XQ2$hn(1c3vr; z_C3PPT>zAQ|I9$=i*=VLh+{#^R!1PtpTyB-B_Ym%r=lp2bsEcm6@W7H9iZd38#Dkz!IfLrBJaV99Y2<7_G+I zU&9AbQ^3+dXS$gfSNgH~C88=BDAJOD^=GJIkc14YT_Mqwx}2IORm=kDii=aDBY&9< zCD;m#Ym2{K>Ka#C>s`68?J;0h7&w2?o=;I3WLY*0Ho*DBFs3Q)uZARP&yrHnk)3R1 z);ie4Kwf>MzMsL}G>spP!2F}gxtKZBomv8QhmrDYxviZq%x*L+r~Z=L(Gq8ZjD`V$ zDAY9lnj2lAHV|)O2SMPe4vi=IO{C}b1us>~CC~Q8M#rc8 z!m$maj)q;Jypw#S{;7_4C*yJW#AVpZ@LcN~vzy`j8KHMr)4CX)yp*K>&-jk>)pOff z$mJO%^0)lL3bFceSqO+}86>qjZ0BLhka*Eg(Q71%)2HCNb`hINymb|dJ0A9A>rqau z#>injVy#BB{^m&AG*qPq_cUGxFABpH?GIz#B$Br|ZbtCxI@9IDK%q!%$sf5mHLg*` zOJrL+N8{G`xw^5qF2d5(V(m+0&J0WGn-EbX4+zfew(PU^nMqbVx6{A7s1V+BhTpBT zt!DRzyKl>Si7(3qTh8?^sE0;G>*Y-}=yh%FHhMi7tJ8K?jj;~r?ni5D9lnmu;qA7@ z^5_E?QYsX?jei`hC5Tr4#PIwWBl+sFp2=`WBh`I1`8()B8~@`^OK%tODuBqF7AEze z4^OxzkmGHaUB>;MO!20QuX7JyDSXhU?k37Bi&%{0SQ7eO(J_vq<$!MaY3Rq4V^ow5 z-nZXv6Tdoc)7Rg~*+wL%eor|$Eq5M^JU&hbDLPZ2b)A5YN2YA+?urDmaX%EoUgk^f zRuX@_pTURiQ!ho@jdQ7E>y*;b^>5wRi$9$M$X*X;a)3>l@0UrVW$WZ?;I@JHKcpsJ zYk#YeIzCi-y1*|??U#Rd6tx@4pD9`cq6f~I?psN3q7{^6J^a7=V33z?9eka}9E!~B zMt)(e-o@OgVES$gImg%0H*G<00 zOl&Ro<9ebweD#h4<^0XRerHwt8oBi0`xN=z=XAaDeKYaP_nGOgzg>Rrsuia%&C=a5 zgHG5+|4&q_TxOML^I@fTF73V2hRV zoVu8!$r+Esht^}D7A_@60$I!=JIpPEma5UeMc+TPH2%kd-*^!ZsLehM0=7c~B6fMd zrU3Drz9rEF?%~%Qju5z`T2IHZd){GN(5@cC$HS5gHm`AYGIZ zY!w9utGlh^LooW28te?2>mD7z0M;v>JSk9Hra|OoCwO$kC-j=kzYH7y#VQh_~^ z;22X%b)e_`awr(XX!4UTW7)U+y|3Di7Sby|U>nHLPLr=p-Cizws`?Wc34pxV-ahbCxR2EoD9lcA~_3jqN* z7@;;AF(!1;Jh%9i%48ole#1jE))V=nh(drslMPQt%_P<>wtQ6SKE zYBdv2qqgi zW=#c(`PNh2KZiQB=E}jcyr_wAhlrw@5dAx)AGR=a3nWJx2t1U@jn~BAnl*r3O5wgu zIcW9c(Fo?FV+<|Oh%43|`w}fv=HKHU>BCEgNWt-;N`p)GiPHjxgFYS+n)#tY;3XVh zu+Un-GFJd+0IR7+>!zBc&=8%rR~RS*P9EZph0zrwjN#@D)B=4gvdom{Q0J{E00jx@ zU=?oi7A7S{bn|%&2GJ3F7bvoO)2v;`l8VxDz8i^g&6NIbFjXV4U?>cjr#6wZEQ>ad;6f_(*DA#1O^vPy z%1h@aWzW=BjrCnavn0@ORyO#hw3>jhcK)UI)D!ZhFLBALL_wnvwIif0qlToT#^EgfUxwZ#e?68L zCcbq6P_70kvfjeVgN(737E;-VUw0@A!GY9$5x`^=XxOOuMJ(5FA5n87+la{rkn^i% z?JU=u9liLH3!A^}8Js*eX|%L_f%=Wdxv`BUAVNjD9WB58*u0%Np#As`w^G}q z8iG|zWV1)!+Qf+2a&Bw)z}698-@!QBu?*-8)pqIk!W#5as)uxpcC-`i5!1N`((^N9 z$#hs7cCy`fjuCam+2?@)x;6Y%lh$n;9kmHZL^V%er3(T=vA`9u@tEmkIDkaOwFr;2 zt|_nXkw|x6$|NOLMoD{G_f|4lK1OOyI04v1w|4s20A@vG=GXBaXl8c=MhYXokUKc6 zJ-oDjj_d(sEN!Ijk=|&K-^DH0M^V>lW0XlBkUQa#1o~aknXdKXAR@zV5 z*U#ff8_5wej31nvM)o&+kgb4vrz54!x?32fhVn~VaeU&^UM?siT$Zv&v4j=Lm`P!O z@KeF3Ar%x1UupaSx-$49ygtwSzr8)t%^%gb{mF$-MOu;)SSp*yJd6j^vG=)HjGS#5%LmF^vJ5 z+X?;w8Lg8WEw&jgWr``k&@GI@avdIEd>(Vf8Qb9=-&`Ndi5i#18`m8gr%@S?Mwy6z zWw|^WN$$fMR<;>~5~p|FO;i(4-W!aoi%#~~O!`&Gt?4vvWTCIveA(9N+hv-X-x%HT zo_b)N`lT`@H8fRwF?nV)b?ZHOdNF?PJ;K1L{QMd>OBIa(4C{5Y#{TP z_!=G3phZ6@*oc+sL5EyNN;1D$IB=#u&&*iG+ts9+Ix840FRU;RnW(qsUC`}7{zKd+ zt=pD9VJM=|B*r{XuD$RfAhZ}FFfZe>DEV5g$XvzWHL15b-@l=4^j@#7z4WQ0Q7U_B z3}HzP6>%N{q=UvUM|Pn=2A%OkEf|Y5Su1i~D^5j3=jY_?fsD0AFgLGsT3S8%?qz%%b!G02PjVIy(gR!;SC zBi^W?&>8&snYa6yO!k>Y*V%;JM#=J$M&@Fr-I9*ac`K=Mlkajw)vCq6bBDa!_d^QD->k$<}j)fZ(`7b`iJCC!%|ESKEb7ay{#RO=V~O>_8% zz-zs^-=7bUFE3>`FXy)w7D;RW>JdEXtwx*cNt+O7>8?Pxi0$RBV48YQsb6iqrZfVhHsB2_MwpTS24xNW@ z<*RPL`N^>5uG(GR{xtjhsCav1C(oSww@dNw_Z}wp6N;0JTc}D2xOUlRmLYbO9FuF0 zCh|6`@vpz>pU+q8;>ASkU>3`hN{(;WE|M$%l}@35n$j8}!f1cg?PPSmvbF!a&(JfE zsEzakX%CoQN9F~9Q^*>+*zFrER{qk3=X0jJ3uU>}=A1m`gV*$z$^$xYSoQDXyRPVC zM;UXE)`!%T{T|Hq;p3{}Za_@O;NE$oitV(Q6g`@IFq1ooc6>ATy7k~P=9ypru8_~~ zuSKpH-uL{9j54P6?fSiBS(BUER!z_J_SFYEYhG^vJTjpmm^HsY1O-o6HnfHe&ESziOG4tNU^tps!1Ilc zk+RDZzqX5erQJq6Yv{eP!toMB$0x;zm_%>ODMkN?~DL|vx`L5Ebyc=RmnHy zQP4bkOFoSZJG?T23$j$DJs{K`k$_tY(HU2yu;#8>YqF^eR0xMs6?o{Y3o{id%;Y(L z`-G2W0%KG_I0k?I#lBKX2>=IA2RD-{)|DgLQU$9Vgdx*nE8z7H#q)x)J$;6sokWZA zY@t%S!_4Q`%8|sz)SAzxE7zaRPOmWMpW2o7+ZEwN-(z$rS9b;*%ZrKxJZetnAlk>- zvnSVc%R;JGXu_R$S3P!->b}BVFAE2`zx8V%LT}G^<6~iV-M-%MZ?C^Qw?STjmt)cZ z1X7Jff0x~`9VR4hpkWZY)-T!MB6$nLP(lQEWGj5Fr`=GB9J~Vm7)np9y>JH7GzzKb zDkw=5yO@Si2#4O>UNnEg%5J0p%ad{3^&(wHTq!|#Q7Gz7!(4)*Fe0_MjN+-*6k)82Ni&=(UH==o#g&!i~Iq~x~h3=jjn3>G{U;3`yUc*%_0nMVDd2g!6MC&aQX$sI4QDi<0r0j zrlbYfNZaNDpa3b|#vgM^CUqZV?SR5Kh;4gRfiN= zJtQ(UZR3K}|{vCnw1xl;$ddeeQZC1wW@|9kAdm=lTc+Kpm5pbMayIdhEYX zNcBwA)Y@?^9jk5X5WeefovWs+j^(%R^fg7iM6i5}z?N}qRB)5qVk9RwNSVvx#~|6% z;&M}i(z==ZS!v9>)?Qev?SolujqW2`!l<4=t3HPq;Uem{et&k%N?1&|U1b2?X#g#S z~G znU;?ID4B1e8I=HXv3DfgvG}ihWpA$F2EfFH`d`ZeIX%M)eFcrIas8z~p%)6hzrkTu z*oQO<1N$)|?1QP8@)4E5{W$-nUebVjp3Qv24obw=?nq|1aDXKFDLdr>ZPqEbp6z#g zs>3Iu%oDey@=t7j^3G2S|HkgaqcH3vToVJGnCuB!C#hj!ObpPO&D$SM!ZAe&0LqPQ zI2jYMzr%ZpUowCrVnXpktW=>W`*SF^cSCI9%38-rK`&>BUi{cLj=1)|`;k z=$uIZJ!s7Jne|t#ASTth#<;=SZHP(~0siL_z9jOSw0;|!bi31Z9W3eOH~0rxaLjZr8^;<=4E4|b!xQ-Mx7F9!2^XC(eFNA~z(+F4lBLwDwJ#ZcF&b=_wW|EU zEM+r4o$_S|nckQjQ(RDM(teD&gbPwIvxm5sPi|ER8ZGg+!(8%`l&o15 zR5!L9GTAs$uWwY#CZ!yfinw*GIeIp?jJ{tM&_(7nL`1aCy;qvWL$sI9=Gtpq{h@D+mAvvE!&q`wvs8`=Q}o6$L3pE*`sZj8TF3LTDtypS@ZfyfyarZhyXOf ziW$3K(uwZ?1!QJ@V_>#9?jLa2E3uANPFWi?8*-PG?p)$9I_|UUa&tgUOdM8G)O`n| zH=_YxMl**T9h>+wwzwoh>juE4qI7R%bWFtQ2?cs&q?SZuP2xt=8X3L;O6iFNJ7J@} z%RE!#RQ3%W0gjaY-BPOk8SVSTJ?`7IFm8k*o(M>=Z$LtD*o*~!`jpbZ|rDQ9VRNXM9A4o=x@yrHjtvpeN zta|MGH0FHN>R{48PsGCZ<}guSBm-#X_)Me-Id~P619$@yNlg+caZu6?8I z8t?hlEk_8n`U5T;X5nGJlcnNeJ{UCke*Vkjt$w|%q?S{0#dIhUl@-V}N@-%o9#wVe zund^FhFX&bOz2E+)0Zl=3D{ilkiU{@H51hq?@2aVuJKt{H_tt9oG3naQt2Tyy(4=d2I^(7jPWET$n0&hBtnUauLByhu|z-%iMfJCV}YX9z|HtO+gY6{X8< zk(cfYhKk8&Q>NW9@1WQ?f?&ibmGm+A(s<{Lmr|je7rv_QS2}f#x^hyNt00mVJ#?*& z5C!ckb^QC?FouC+ue!fq51+b-zbEj-pghrfJpY%Wa=A~X`G;q7#l!TA#ve(;_Y-~q z9Ed%%MIXrP7LVW$kME)SIY8e{AJ@Vc>&XTGNuKb;@(alC0{4KPgnBvc4eO+ zEUY{%jIbpv_CW(Nz!rc3)(49*NQFv1h3(=6Dy5P8JQ?l&Xazehf<=L9#Z=*mPio0P zzf?Nk^s;d4p>WF+xipQ?e5ih5tM5p1Kq)w4t2iRAHL$w%SIv_}odxn=H7{x&#EBF% zieZ9WP-HW{R;vawMiOGLnINHQLQ%5jkJ+ zFiK~N#pqJh>0MO$Nth@;YH11jp8=A8dI4)7KFeUMBf1pPgrw46De{PN$wxtuGT`cG{#ZUw8I~9Abmb463<}9%|eqqvJ#vSBBN$L3jFigm&0A5SX195 zJVT&3Ba+n%r7Z2}OWMIfRKx18CYdyaGOF70%&yZ+%}-gJ;GkGgRykMH$mvhq(N(Xu z)2#5(%u>W`4eD$uP^;65xfB1*=Zx+0V8rqq=&G_( ze@JMV`s8W$ZaU(0V6I}I?Y~uwQ0lyoaPc+jfmY??j|f5D$;cP1@* z*0s1%^1}J@Lf8BJwEMz-nTYvt)8ep{>hwbKHG09bBoVsYjq*UL2zT=Ktk(1*&9w~f z4EisG+M;8{H_17=T7?vz#Yd+_;cdn8FG_SsC5bLMmu>lPGNpX2C7_>07P5t^o<&l? zQj#-z<@TaqANZ1_s*3sXsCK!rT2ftqpyZ0zsE0^|z!%^@m<=nV1ua8-BOsuY zoYj54ym|8g2LdUWjN={iY-0-_b>IPQ^BX=x{f+PwFG^FNDIl_quc*nQ<97j3Gf^fa2BSTV92Gat;TF3? z6f<_+07z$O#K(r#BEYkw)>fC9?lMyC?NxDlR$-D67m{Y@;l%x3;)t?Si2*OL!P=<# zag?Bu>T7snt0DcHm$uvWwi8daTi#6cv?DRpz(|zbiGv`qqqY0rZ~{R?SEOd5Y#;V% z9`;{rqw^pqz8iQ21Jt89v?FCWy?kQUYYuIPArAV*HIF!EGUcp8C2ccG8YW3t+gd;y z-}C)V?ugahR3!uKS6*W`cf=8d z6V$8W?uFM8W)&Yc2M@MPeiN8ltBry{MoU3we0xmehz@IE-2S+%z(itiw_Uso18>I8 z0C3xIR2=AsS2dPeXY_7b2rEIQHNi~-XO&z)1+fxMCnx`r^yf~_bo%daS}4~XjvJ-* zgO6kS|4lN)ait(-6GxEHpBZKJys0n{`Agd2rL__)XvV%LB7(BHmJ*M2TS}U%PyDl zPC~P;Rm%>+%M@M9ewz|L3YGo}D_E2(NjfXxx~lc%E73x1QI{+6*(_mQEeW=(XJV^A zy;n2e8HCa&`?FBjjNn%D71kgeYsJxPL7r>nlWPL)YgMT0z0@;x3hNw^eNEBp$uDbd zlk4iu>s_cDeYfj<3LEAFd#m`m8>5%&4OJVHn-MLS8?(`IrkjXU$V+ebwPyh8u890) zTc*{jO(oc^&CAUo`=kSA%(-kLwT(t8+u8NX)rXJ;8KIT4XylR0t^b~eWYlR%g*nK@ z%w6=#!}|oe?zZyX>e=%0?dHOV?x^_XGJJP+n=%|c>(?i$g_rlO&)M4m-x2|ZWh}Je zW(-t3!EGyrSq#M@F6JE@&%Fes5DMYlhHLocR4nJPUHa->>d8H_tsKJ3eN2}9JQQSA z5kSyK(7tHZJ~K<(b@mSJ`yRvOl=#2aT4i{61K7;?gF`$+(S4NKW0b-%=dh)6dk5$K z-cc`|GMb1A1XM(bEE!7oQ3^g}U%;q|&~%HZ5>D~(HwX#Q+evkQi z`iDSf)Zvbk&q%EZ{5i2^gWK@BT*1UXY=<>&b#$&V%ntK>(VsnGV}s}s^GPX}KJvPw z%?n&P2UhC@2IfhK-kI+|L@pLDrK#^#ZrpROsF{MWDAzG89S#3Lj&*mYp6kKOIf4Bj zcxaS)jr!w_cvHv9-Ln0En=Ho%@96L%2IKh}kuJd&S0`6ok1d4Kxb5yctJMcFGw`w1 zs~HBHpIc-EN?UO{688V0$;5LtO$c^ASfNV;2Xh!6&$%#fekU@QNteFmql( z&Jo(F+CXP$wJ#5G&$#grfZdSSHZwXgdo1Q*)t%h*ox*Q$o%YQy9pA-JHxX+T(Zs_Q zH(?zbB5lf3572jK`FWd*FE6-8R-`*CQ>8Jzj=aY`thC)6kBMA3i6}N_R4?8hs5eD! zXSi`^Z<8E4zS5e+AVqPzO=V7^w@ClTQ3Lcg8R)QIp$lu)uz$~kuoD^s)1dXnBRpr8 zD-SVxrvBx4z#VJ%WBWfu#3(p|(g*ZzrI4u!HFPMeLswWcM4ByDDeQ$Fc!kt$D-ILq z_%IE$z97v3iju#HIe08Y;JjptgY$W;G+lptKeGP0e)fIodq!0M*NjH`^x-1CG!LJZ zRddkW_qLuc^+WtX|1t%7t}r#`4%5=2^W4Ao6y1K6rgB@5jvomFgo9N8090XNaVV9z za6&L~LnMO%m|$3?kpByBvtV-z@&J*=g=PPr@HV@sO-U7AHK*^RXS5SqKQT7lHq~1U zhnrqAm$5ptv6&B94_#i_**dCT-IW}!-97$)2D1`{&PcVJE5ih^8;Y6}tc9ZgJ0lsVTq{GsvMa ztZM*G&%CYwl;~7AS3NAaaR0Bno%VScl+Z6VN6#D@cdIqfckSc%ed9XcA5Y`3R$FE6 zKN1W7E-q2*ey2BUNAUX>bE~8ALanm#_?D*UgJo-~gr5z1)r#k8}JH80aE7(5z1K!MGVax#8SrCxs$xV<|;(YF4MkkIJ$sC26i_ zr=>mF_6#Xyc_~(B<$ClHXBCyrYnbJ_eP?HtjbaRtpDjxj=hgm4T1hpX2#l2(y=cEK zYN0flhjpTCkc);Hv{NSASv70hs`({nT(mWpb6b1L*8ZxN-9Yv~Ehi-qmiEIG$7!A(77-_q`_A^1_X|)Sz%l9Y-T0{{p5<0+)`{u7 z*n{~?O&{0ksK2G=yod&P>r9K7=}<>2FVk+A37XTGCvbn891P&VQ8|p0m+Jg==lh;) zG)kv^S~q}F>grS>>eSpYEvcT>IE614O)>k+?P795=qr8(lvi5eenyb-{nnq+iTGyG zJhSRv@~hY8eWIbc5cj(BdROy?*tt*3xLQp~)Pw`k)BPMng%8`R2l5uTza>vs>u~He z*56qJCEI6OIE=VVob2-ynUglj>tMhK&VLYlKWc{nkS(nWC0B~`9X*d*ybKq!-Q?lJu05{V~t2)T|=_c_{bmUDLD88Km#{~9``hr(U$U{Tm-@~8X znw*bOY@~r-?g3#=bI-iB4b?K3NYmeXj+al+MNnz-B#!0t5Rq%A{G?=bq)e6t&L_pZ zGOnflYTbmYp?f&lGf-MO1K172IF@*9?QLaI{~M2fs)qF(_~)N|nFP^JD3vf2)qNcZ zW}(&;;oCF|E#j}`F#xz7Lv(900dh1YS2%ppzX<=)8AP@K+y!`eJginlcQlveT7L?Z z0HCWk1{T0TAGZbg6=|;02GF|qovcpskiZylIUkJQ2lT%ppM~wjro{WC%<|xaj>VA# zPk|3iz#yHr%vB~;?c{+1DFR{gBM0#)*`%2XZMY91WO5>GL*h)T*k4Vwjzo^Q6{5*% zefN96GCcA``JJH009XSU5tW1dZ~|Z=c@KA-N5YjO<^nQ+WDEhv1lUlP)ZhgWYLw{2 zUcQ5qDfZD@hUJ79Q(#Pnh8aovsxWe;3jM>p8Kw#CC*!nWjNS%n{*JaT8kmM8$^!|^ zF-bs3ITm0mikkfBvnVRASo#ExKVnAw(MAfO54GvJ@%pt*_|_wsAO zAM?PCyRUp)N}?VWyepc<*uYA9S6}JyFVdz+7mV-Fmxfv&Ju~LCAepSK5)t!ApfvV(j_6YS$ z39e zi_3jcui6>YX#)=d4->H=aE=46dzee%g`rBU!4@I(8yrO?B0?pe^ClIkYW(ObZhLGe zf#4PT4}$Jn?mP1nQ)-7eETvnzkB=Dg$`SQ zoLPXdX0AceRs815_e#>`0eazFKXt*Fl9#8C!MS&P+MG09KHS0rSh{}ffD!bUXoL8( zA72aB>G`V0RA;n)<){uk3W?p}Vyc;%g^z1+i)G^Xcx~LhV+2g8Nc*QJ)k=`nd9;^P z6of0~QIdGjT3=)k^pptSA8_9YA+@HKHHb@S(5_i4e-~RIsV>M@e=#4`^<2vPW zHX?#;U;DtRt4+tEq`||TpOab_n2!F#@!O)RVg8{RmC(pz{L4mwgN52v=I@iO-wP`6 zYxAT$@va@2-$Rp*gXZ7K-Ip;ZZIj`9Pi?MG66!~tv)yw1HfW-~UUyz)o@5CAkcWNK znI@a!eOo~)amdZuM}2JV39GXEX`FRHLcz{A$VhVI``L3J7yW6{mh|s`bKK*9n0#IH z+Kz&LS;xw>8LBPjQ?{vBIy)>zx8tIAUyB>EjwH=rmI{5LJnwI(!Xu9>byK{hKe8`F z_uCimyXafAeNO3nb{D!}Yr7d!uC|T9+n<%5oC#>J!@hOy;rXFBQ~Tb;#d_OS?YXDi zz58(Wq#W^}GqkU#{LMr6DHhx|n9clm7fA6AQl5TU8kf1p4d^;ISMvIN+4aym-4$nY zg|T)1@3A*k{$({t>1{6;`UoT1eUm!vZH3zNJQMqG=$YpIO#Azv>7MR~mg)Wrr;pd2 z>FLV>rGK}DQqSq^Juk~&iSE`1-Y;T%-ga}IpVvSBxi~)x|NC$KL0_ro1Ay<3&gBcM z;s2Q(cH8kAkj?|eLZwH{9|1ogZh-BY)~Wc>4=XKT1&`~Cg>yV?03m)Lzhpq#An6Nj zARWjGXFkBS#gjHIkQLvSSR)YqDUi)QXs?gmpwx{E9Kib&#Gb~bI48K7=uPu+5+srq z%m)g*6EQqp4q7M;R&5Pt1S4(F3*$@ta=imG>Kn#;G91+XoIA$h%^e|{>1laim7fs|2 zxa(+{`T%yJ`L)D}GXB%FA4ksngdnNR#eAeM6BP#=qNwMMD%fCR;WHzq(uy~ znjncwUu&4w$~E?9ON}*YdB=TW`Vr(3L{!G*dJM#^2>R zk+DZ0tj6P|S>7)3hL`=SWlsn>iKnB-K~uN)FiRj-Pe>?Q~R6ZuCICBpd}TZ~1*K@zEnAdMu&)kxk|Fpo!)5MPp#M{?T$n3*qGt}R|I zJlVifn3YFYM#fL}Ilc!!#R`!pDa?Is#vk=0ksCA7S~J!9GwU zzaOQtr!~J0d*o8!$d~WSxO3(8A|{95fk<3JE$=|_@Nxo4vGolhcFEG0g@T8wsD3ya z-DU-2&v}G<_V|Q3_XLhugfW?M%I=am?|i9jShK?SKcz2BfWIX)wg(aS8WpipBwvgp zpG}LB{o!DW6(!b+*v=5)$tbmD9fd2h%|A5#`1k{IS#u#&4n=Hfsme&YT+z3`5XW(a ztEu@hoGo=1^^w@XSy04&6IhjGwVy7ft|{JDT4ka99=S7lv2Mg0VyVU%q)>e6NovGt z>O!gVA_ppy&NLff3gQj5R>P;r%1=^Go`~5%{TdFzNp6*TP3kk{ayRqxsk<_mf|v@B z?w8?QLx!9(u;|u7Fu=g@Wl=AEM~)bwvd<;_(-I3&iNRDvr6xc1?oRd{V#&QEqzMU$ zL-IS&*%k>&^0mPXm||)ds;hz^0B@rA5oCJPYN{Y2q8ExtSR_iv`QxBnlc0^Mc&93l z9`YaJ`oKg}xR>CvO5sFAT22Ozc;bS*ej0?6sn@7U*M-`wEbv|A3AUD4Q;(tHuU7nY zOaoTrVamD!Af&rw1s!mZ8N_P-cB=Iql}gL1SknQgs73mt67fw8>K^;^DFVVs)vqTqkTsUxW2)3q{W|z)|W9QK)baj zwJ9{SbuOYcV!c(Cy;b_SHGzmGF48U0tL?>%p*gLiO$E}H_1fm%(3~gG&ZpX@&l3W+ zB=Ku2bXjj7tZOgwDrdxt()KFEAnZ`eOo`O!V3%uZ^{Oq%4DPfp)P3!EK5N6|?UeN* zEm@DB6mafXZ$7~793HPXBJ83P=wjCHs&Dq{iYV*+(b2d8$!~w{nj=Cxdv4l4&xKn1 z?_}zIf2}!3E?RgEx_VX20~X@OX+4c4|Lx$4f&@H9S|J&RJ)0oLh*Y^f^juwcMwAD` zy%}Rjgd1=wCa{+6jr7v4cAu%o#Dr*%z#t_wRmAM%=*udP&lVGUyTC*SeZuVHER|32 z3=aY1A!lVX%6ATB27dGI&drN`vZjriZ zZUvzP{kI}3KjDHVKcuTU^xdpkJS9SQ2H}u(Dg@BG0lD!mLwevxwUNhcof5QU=4k1LIixnGQSmyO$3 zc5!tKdpYv0dL_4_GcmGxrI-pZ4-Czc8-iyjvQRj^$X^EwM{J%Pm7j9MG>bTY+R=Wr)G(J zcW10sLM;4~h+xGn5}L(UO=mt(oRHq8^0-taR(g)jhiurVgSZEV^EM9!*f`x$&*oi=ezWy*-eBB_Dk&vSdDof0N>bV_QK8bcFy+^af3Q$R zSYXPY;vT7C%@$Q3jOaMZrwE+2E||?xu6<-m6hNJL3eiAJk4wx@&u%DXMC}y|TaeP6tY01nEw2&V3)?D`U@sI_&Fs0L1_34d5Sp(Bg=M;?S)IcP zVXA&ad;P(f{o_6U^?jgsvAUmR!7y5uRSH10xUp$dyH3~uJpqmlR=@FXHXWnMXHTp! zH}Bf>Lqjb-rEcIAYyJHzvCO5>ezWA4IpX3oiRBPmd-Mb4yr=3I&EkDUa&-M^V|J0! znNGlIg=sf7tG@Xn2@6f7C2zHOU$KgB6`{JN(HvrSF}WJeIWM1ZJ>kh(InOLC%RkXi zkmJRnH^!wm`SWsvNZ3mtCi$!0R{!H3ck2EEa~G+?HXie~Sn0vl$u*%Zw|?3 zZpo}5%FS`fz21NV1MJK}%DLX0PL6p$-pf+%$T^+t%6{w6?&vp;-uz7E(w><;?#$O- zt%K@FVf7_RR0Z12iYf` z;tajj9)#}>%Kl#N|M<=cLL^ZL`aM|0*We@Zx6?V>qu*)kVAieW6Rkz6?dPa=*a4sL z_wMdysXr@pPrDOG1TfOA69Ik2Ki_@R9JkkqjS4b7!~~^PCVu54Z|Ad1@Tf$v1NA|X z#$pU$YU@};-ktGFE#Snv4Wt9rB`Vd1PIM2>)jtpLR(oH7#nHV=T9BmDB{bHo)K5*H zoWz^;=`In35CLp`PrrTjFjwHszV%&i^6@N0y*5w+Nud?>537UFNbQQFt@dpr@{g0) zd&<~g<=8OS^LLN;|AYq zjh(H%&E4(&{Tb^GK7J`)Jo<#Fju=6HUTIz=$)-_> zVg>8;YgM#t+q!*gP%K?%lk5BgO3% zxbNY_iyJ>qPB>TP$eTNV4n6uQ=31#szm7e-_Tkp4cHa&@y!i2yze6RDKE3+&wP~AY z4?n*A`2^qprjI|r{{1oSgXRxlfC3Kaj(1h$35ldlMw+x_lJ%uTNt32X>136wPzjQioDmu2M_6hJCOtrgnHiT1Wl3h56n(i7 zn`&}n=2RZJ`DUHW|BPuTTydVo5uJIy>1RWM{@Ki)kcdM;JBbD~D4}3BS`eg;?qaAW z`tUQ5q?0D8sX(1>N=v0`JyHV&03cI{1vRiE4I&mSLI5HRRP!mQFxL7HuC}hlN|BMs z13(RgtZFI)^Dw}p1+z*q&l3cAQrWK3((2x{TZw8_BvLFpYZ0ajV(JL(81m{N^RQ%X zy1eLm54-9*5-VdD`4!2ci%K!B2@D*v&bI(aA;7qWE=It+2A>(5y9d+tj#LOA!%rsw zNIY=_HDD^utAY@+8Y&7eOy0)|f$U{DP(Hj!1g{QY!4oVivQHrdv~x%VhbY1uvO%3y?Tu7;f@GF2P01KdT$2$YPy|BpNX#K9x+Cm#qmvefI zb=K9Ey{5^K6fvdPXts@Z*JsBam)Z^=-6z>}r_1$Hd)M8tS$Map_uta?4X5Ex2HvaN zd0(x#t%z?k`Ltg1YU$XJN4{z0*@$(cj-rp=7mqfQZu%pklg@hUqo;nT-GcK;_~xdp zo+Ip>tLHg3V)agY@T#cvnXimr>3HpM7e4#$ywAP-kI5r1e3{et#CnvjZ%?o7MmO&~ zdcdR0{D1JA9DDKCOD7k$;fMb>zRunkRaWEGm$!a&?W6y`_|SiEzWmMaPM$H|cJ_z4 z{=x5m{YXmvsF%J13h+4yyqf~Y6F;jBkS7lu-rzQ4K1?*1I6SBqRpxh&T?72 z)DeMgJxu^BWSS#d$cPrIa1lCuA*&LYxktpTfkA`>z${=dfpsN>*%@IDF&4HGV(^4f zOku1%K{OYFO%UfQf(g3Fu|{O^7|$Aj0E%|1GNN%79b5#kP*_COL5pQAnxJ!>$cQ(> zQHmz~S@d#9fEsA5g*ZHh#wfsmLn@JnYXkrxN07)uWU-KJJYFLx7LE@tf@thWh7F70 zs%zP?Ie26QCNs&$1^UJjfSY`l46``KOFrQVucE*#PuWVa!LnfbN~0EQ(Z)r{u!?bP z#55v62w)X~AIhrED3Mc2VXlyvscfQqaNrn7;IL)8Tn7#mk*EQf008{LDeYmO^Sl_muU=Xs7$(uUQ+X_q%sv91z8Av z*36kB5{F**T2|=rG+O0T>LM5rHBPQ$g%qfo6!`hmby#4MYlN%|?J`Xg7E_jFB_Pt4 zHjil-LLC@&Ac@3*OS~o)nrF=^{bZ|J#F}s?n}9tvT>kggsOu5gZC zo4p0wH(?s?u!m98U`l;>#3VK^cuS1p6bDDZDsHihAH?Dp%Xr2s3GqQ_jN=@iD8@SO zv5x^_W1;+b$V845kBf}tBx`bz!%A+llaYJkCrf$Cca1WYuZ(5vGC9j!?s8)#>g6zt z8LhAh@|e#&DKnQ8&1`P7nycLAIE(qsl|%(LEezId+*x#B-Lr-Ne3(2t!q2&r1fc=# z7eOC-M$nZr1&^Fv-xa94@#QU}%I=Y?S zaGBvtK75S2yqdwtecRvs^VlQZiBTbk7yk@Ig z*tlM`ua(Wg?8UwJ%jYJ}w-v1J zbgrXWYiywf>>}uSYa<@>B(^z@n8e#m+tjJPgf0DsY)3ZQ-1Sb)z3B_?bV5s9=!_*> z$xAs&&@sH`z~somV(VbXTM`$8wg5eP?TV*c+VrNR1&Tn)SVlGDiPKhS4XzIudQxMc zq?#z-O>bd0B3=yOBukMPa3M!Lu&tfC%tUo_afd9LFmg@j06LDwk1GSwci}|Ub5T#< zB9n`PVCK&qXqc3w{ShW0L>6TT%Hs~KFOwLiLCFsIzzcrxgfG0|^@9IRZCARLlO-t& z(YW*QLirId!+G8dr?I}DGzuQ`tE2nA$^JOl)p~Z%bDLBX zpSn0$3V=nu)Gk%?D$=Rl_kc}I5x5`t1am8U)w6!)$lkd=CE2pa$3FEv#p?7Uz4%RL z)n(<1`fY#z`O6DJW5z<&$tQ1mwl7@y%WwbZ#V&=aSs;vJm;CJIX!(^d0bu)N{SSW+} z$5=Jkg-$3LP`HNT2V_KuePwurtA~VXsDy8*f7WA#11N?9cZYO%8iYBhYiPKIYG`14 zh(CQua(}3WhA4=oL5NyLhKHDki8zU?)^0)gh%Xq4?skU00B8l0VTW>fhZhm0*chjX z7uqvnpBRc32aB_qSeck~w1|t&V~fR>i@j(#yBLVRD2!zTjFmWy$H+6q2#LtZj0U5O zp4g0(v2}}4dy9ZOtmrz`$YrC?c!AOQgeu2|-{)qE=Z$XhU*QO2amI~|SdNi#j&SH+ zXSR-D*p7Mvj_`OOayF0DSC8g+kBS(NW44b?*pG{-hv+DR>PU_S$zAvckol;PS;&xe z<5 z#YTXEL{b)#V`!M^D3U*jngB3=`*K;Qf+Ff*&AcmIfxIrf{K7@QwsQWiK$ zCTJ?Fa(YiQ537QLiJ*Oquqy27QA$L!F*C%TH%5;$)}3?soe-HnJ!p(-HWB%$9|D@1 z`|T6&8#5uhqJnr@hgH!7mg7&5`gsgVkz+f}LFW*V4kKZZIakJ_om_^E=LlqUK% zK`Mo%3aR6@sH)nhxcZ`e6RHHZtNN*_kYTEYSfH^At0Ls7wR$j;YNIkSs(N^-%4$cq z+N!FgtFj6-%=&&lG!L)$Z{9?CjkHTy*-q<_o$ywy$R}F|M>QU)a7-$z81ZmWdaTw& zUR4OM&e*F$v_|4WNrgm$&$(3XiT5`~$T<==1z?z~+CwXkr+DDc* zgr~VH+Ip{Rq%5zeaW*KfY6MumB%TMEu;fIRwr7Jd6q`gBQ%4uEEr^dyx04hgNE8sL zEQqb?ffKNGu!Ood{~XJ6g+w$F@UM{9NR2R51}j4bD|NCJTQw>U3_u8AISpKiNF_S6 z#R`x)(?V9+oVh{@1tX?2$benDvDH#pZsoQ(3Ab-6w>UX8pyhauC!TC|QL1UOcCoO) zFt>pVx6y#F#UQwDWmuXgLx349o(UCF&;*bxxsgk`lM65@VY!t{x#D0hi*PSY*e;nX zx|$2Qq{{#`Py-f|x~QwVt;@QvySlOKy0QzqvunGxi@UU|ySKZ$xy!r1>$}0*yTc2- zsVe})8@%F>vGstwt4lIkIsi74x)vk7)l0qCYrNXDv`GtlB0E<&BoCYww9_%Yf|JaKtE(O1nDVR`Exsq$S_S*zRQooUFx%L1s?I1Cl$te5FECejNlUrGE ztH5rxz&FXj5A47Z+`tnI!4xdP6^y|bticw{!5Q4a8|=X$48kKExBvFR7F@NU8zmp? zwt4EpFDyFyYDE}zosxtpc{{Rn6;yP!W_VS&RBMCV_g8E5l5?wErT1^Axa6D^y{3MFESlLTiod3y|r%GD9ab|97rMtg&Fs#svtuOjDOl zoF<8Tt@=8spjUqrh*EjWdwbg|E^`+vi?D30vVOQWs5PIfRi82&o>gqC_7qBoaC1=H zE_DL5SamEncO0xnN^Cb6hKP!E{%MHR>csNIs>wLTtgw@Ik;Xime#a6+7vY@QCguKaS8Yb_Ut*Kn7-ucNi7tG;E%5$upsD`}DqUy^6C(F$& zqt8qx#@sVRTdkV&t1WoV;q0~AT+Gp|%dbk##Eh!T42tV4%)Gp*rRHx90A153 z&C?2!(;N-dK}{f{f|pme(?*>lhEfD0pu#X+)Jz>9aq`rNK*>_Q%T&!ETyoXw5Y<1u z)m%LrmGw|^S7Bv-wbW#-Uia54;$#eYCY9|{m#!OOk%up(aYB*rN4cMhUsaRS~ldalk8QG?73ysRmvK`xZy>31|*L+a~+qrFq@{_5qjoP>k+}XBn z5G~xkz1hbttijFPqV3!!24aXU-P6s7)vZ&%jor6RtJ~e&-Q60{-PhqQ-VjUL;C{x~QO z&#AiN%hmedF236`$=Je9M$7$xwMQ zbDV-eLt(j&v&gh=M+_b;aMM)GL_-}34?Lzk~o z1Vm>lxYxOv{pL*nj_Rqd4TzZ+XnkSY(s+x~<{R}dD_IMl$}<{YOnwW*o_>ye4OvaBlo&Tv9`22<%)y+# zaRo4vSvC3uO56udH8MmjNwLgH=o+(CD2OwL-P)%N=Uu+dFX%Am{5&dJ5I zjspa)gd-8u)&Sas)P>#8xzUHk1FNLP(IeaE;_uGniO2Hm+l~<<@cWqZ<@^8x3PeVL z*rg(aUillyu;IXk4kM1kW$Q_`LKKQv+_sTWM*trs_VZNC1OtU5rx6e!Ai}T+=!&pJ zC$c7hg%CT{yF`s8Oqne$uzb>!rcGrzZysIBv?-xt>`*$LO4VT1tH5sA3P&y5MgMPK zxqhVz5$ZjiDIFDQ0w4lggaC>R17P7KR(?3?WmU`8#Kv(=jE;b!f~ zb?w1y2M@AjJKgT%$dfBy&b+zv=g^}|XO0Q3@#)yJYv0bjJLb6si;I`GC>*%4Vy&4c zP4ZHWx#=1svOvia{Ro(ij`xMu*v?DzRYD0}16mT*e&ivj;DV=A7aU>?MkwKg&r~#4 zA+mWx6KEGYRTgSEoRuIm)1bqSJNZz^--x^XSDuO19C#vpDl$^zjkXmd2%ls)_UPk} z^OV;i04DrKS3=x`H6&9X26G^U4LVXoZSK@yA|?0@2;^S1=+&i^RHEY!0Pm4$WRHn$ zsU|!n71rLGaK<^Fg(41S*@GZ`6%u>>_2y=uBrXD11b+pG-%xVOr>I9uav7zdgTORf zOp!+NW15L(x}qabzNzV_pfVX}lhe=$C?c4K8t9jf)#<1ssjeBTskcD z6^`nuT1cMy53g3)N=OmKF3Rhy#5P-zslPTW?X+6?D(qpfQVXTA)v`J*v)a-M?y%vO zYp$|rMyrcD=XzU;m+xvjZo7%j8zjB>nrkm(q;9%uzBaPBF1Nbw3owud8+`D>5-Mz1 zzb&GRZ(jZpT&u$j*O+j|9A9j$z}Wt~FvY#N_-)7X!i)0Ca(d_T%Pf)PT|Y3_Y;$%x z&0NdPJon5S%ggEv^w2~XZS>JdC$03-OgHWH(@;k(_0&{XZS~byXG^X1)?9b(_19pB zE%w-Cmu>dhXs50A+HAM&_S8Pi!`s%E=?)vMn$1eNq zwAXI??YQTz`|iB=?)&e+2QU2a9mE%J{PD;qul(}NH}CxO&_^%*^wd{x{q@)z5(of0 D`({@V literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/orafce_documentation/gif/VARCHAR2.gif b/contrib/orafce/doc/orafce_documentation/gif/VARCHAR2.gif new file mode 100644 index 0000000000000000000000000000000000000000..8517ac93ba9324847bcc7bef017bb6fecd3403ce GIT binary patch literal 1986 zcmW-fdt8m#8pj_xN`#IgLyb%9)H{YOm&(bc(PgJblX)q(Be60Wm!`w<=@?$)QqhNY zHJT?Or)*hGln`TvNm6^akl}Tk8qQo8w}x$u!JPA)m-WZ`thJucXZ@b%`};+iBZGpI z*L6yUHrLY10a9|AW#4l1_}oSL6M*kr~oPhm4k|)N>B+j z0F8mhK||0aXaoX4Fc2IBf{-8xbO4=!&Ot}eCFo@Q4a9&r5CTac<6>wt2AlyhkVcIu zz!b(5&J<*dWC}4An97*SnTkx6OeLlP(-_k@(~xPBX~YCD!Ii**1-T-*LRiEF?$ z#x>40 zBWQ-(26AKW*W#~J|9b06@XXaay1KM}eOHfw+0E5^8nWDdV@jfH{@u8xA4_X%t|@EU zHt5~W-sVH)wu0f)#BAH4y)8wfQtTz>TI;zTK^gbjY_<(Ms%_rt?#l^Ddf`B>p z!}~Aoi!Y1W8FNJFm5H@!?e-&;R}ao>*z7aq=oi;(7hMu_T8>tISGW8}`_3tK2ii}p zdUCJ5rS9O(Q`#G6A?#T7e;TqqytZ1FSO3tI=d1AC)|#JMHu<}p?|v&Kb;Isio5lrY zrrbTZVruFKBl4b_uO1k2HuAqK$3kc5wf@ zsi1j#@+9gKoxH@=-*?w$m*bn917E_e(ku z@nOoYu=()EynI(n_E5_PSDR=00sn$|>3-dQ`gx+=+nmP(gMyNCCw6uv7Xkie&|p2N3&WGbAW_pNQe=T6rMSKkl2CYiZqK|0UW){i%v z8W*K(>+88MBQ)~fZEMcT%**>MC$m%cpRP~KvsR27BgE*Mn6y1L4*UBb*Zt6~;6VRL ziTPBs*c$L4{>)6j`!UU%?D5qtzlSVYc_E?5&w6C~{b{xRB9#1N?Qc)6s=E=BHEerB z)%}iz4^Jvd$A7u^kGD5IyRtap(ZWedp%NN2lFV_&1eKTVk%% zN4yc0zRZhF2t4!GUWGOB16>xTH}&4uXLP)8!Gp(VO&PHc+dK7tR<-!tnIan^ZEe%+ z0arh_j1F`=nwFB#;k3IcZAheh0mM(guxP5sg0iM8b6n!Z^8uBkkIV-sJ%8=Zq5fGT zd#~SAS+>xDH=Oni9z3S3J~VGjSY)XCcVk0>R@97%TCMbl!tCVpu0A7Ld-N=xF)w@A zfUqCK#xIy(7ZO(B`yRF>+`lWzR%hI(z2LEbg!35mIIv)^Q)pXuVOiYiM7*%Z>gMI# zIq3Mh-~Ctl)$Mxb?bG^UIHhHl4~u!>doR|5-!01Uo8BwyMW?u%$;X?PrycvGSIO+F ziA8g3!oT%hb1Zp%Rt4{u=+yJnLI0{#kHT*US6yG5>(TXl z$4ZlJQV(byRZ=i|U2xBhrpfKI;u=iR_1$JZ`m|qpe)u<6d^T=yiN@i@zeQ#^`D}FV zQJ6C;)igN#Y~G_4uxfTx&>sg&ZSHFa&1$~nedeov+nph7e*cBxlSAw)tXXcEyKlI8 z)VrLUIuu92ldqyY``G-TV_vfjdJHv$k17K0I Am;e9( literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/sql_migration/gif/Inventory_Management_Database.gif b/contrib/orafce/doc/sql_migration/gif/Inventory_Management_Database.gif new file mode 100644 index 0000000000000000000000000000000000000000..6b690c5b0191bf1dfcce99f3b3a6c76464859bdc GIT binary patch literal 10307 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 literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/sql_migration/gif/STRPOSB.gif b/contrib/orafce/doc/sql_migration/gif/STRPOSB.gif new file mode 100644 index 0000000000000000000000000000000000000000..f7b1bba9e22fcc5dc5a8bffc01b4bef79c8e064d GIT binary patch literal 2205 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 SKk@S4GVZ@v!u@|e{(k^laVHJ{ literal 0 HcmV?d00001 diff --git a/contrib/orafce/doc/sql_migration/sql_migration00.md b/contrib/orafce/doc/sql_migration/sql_migration00.md new file mode 100644 index 000000000..565b044d8 --- /dev/null +++ b/contrib/orafce/doc/sql_migration/sql_migration00.md @@ -0,0 +1,65 @@ +Oracle to PostgreSQL Migration Guide +=== + +Preface +--- + +**Purpose of This Document** + +This document explains the actions required for migrating an Oracle database to PostgreSQL and provides notes on migration. + +**Intended Readers** + +This guide is intended for persons engaged in migrating an Oracle database to PostgreSQL. +The reader is assumed to have general knowledge of the following: + + - PostgreSQL + - Oracle database + - SQL + - Linux + + +**Structure of This Document** + +The organization and contents of this guide are as follows: + + +**Chapter 1 Pre-Migration Configuration** + +Explains the PostgreSQL settings that must be configured before migration. + + +**Chapter 2 Migrating Syntax Elements** + +Explains how to migrate SQL syntax elements. + + +**Chapter 3 Migrating Functions** + +Explains how to migrate SQL functions. + + +**Chapter 4 Migrating SQL Statements** + +Explains how to migrate SQL statements. + + +**Chapter 5 Migrating PL/SQL** + +Explains how to migrate PL/SQL. + + +**Chapter 6 Notes on Using orafce** + +Provides notes on using Oracle database compatibility features added by orafce. + + +**Appendix A Correspondence with Oracle Databases** + +Explains the correspondence between PostgreSQL and Oracle databases. + + +**Version** + +Edition 1.0: February 2017 + diff --git a/contrib/orafce/doc/sql_migration/sql_migration01.md b/contrib/orafce/doc/sql_migration/sql_migration01.md new file mode 100644 index 000000000..4941b07ce --- /dev/null +++ b/contrib/orafce/doc/sql_migration/sql_migration01.md @@ -0,0 +1,420 @@ +Chapter 1 Pre-Migration Configuration +---- + +This chapter explains the environment settings that must be configured before migration of an Oracle database. + +**Note** + +---- + + - In this migration guide, the migration source Oracle database and the migration target PostgreSQL are targeted to be the versions listed below. + - Oracle Database 12c + - PostgreSQL 9.5 + - After migration, verify operation and confirm that the data was migrated successfully by checking if the intended result is obtained. + +---- + + +### 1.1 Setting the Server Parameter + +This section explains the server parameter required for matching the behavior of PostgreSQL with the behavior of the Oracle database. +Set the parameter in postgresql.conf so that the database always operates in the same way from all clients. + +The following table shows the server parameter explained here. + +|Server parameter|Description| +|:---|:---| +|search_path|Specifies the sequence in which schemas are searched.| + +**Note** + +---- + + - The server parameter setting does not need to be changed. Change it only as required. + - The explanations in Chapter 2 and following assume that the server parameter has been set. + +---- + + +#### 1.1.1 search_path + +Functions added by orafce, which provides features for Oracle database compatibility, are defined as user-defined functions in the "public" schema that is created by default when a database cluster is created. +This enables all users to be able to use these functions without having to configure any particular settings. +Therefore, when using the search_path parameter to specify a schema search path, you must include "public". + +Some data types and functions added by orafce are implemented with different external specifications in PostgreSQL and orafce. +By default, the PostgreSQL external specifications have priority. + +To implement these data types and functions in line with the orafce external specifications, specify "oracle" and "pg_catalog" in the search_path parameter of postgresql.conf. +You must place "oracle" before "pg_catalog". + + + - Before (default) + +~~~ +search_path = '"$user", public' +~~~ + + - After + +~~~ +search_path = '"$user", public, oracle, pg_catalog' +~~~ + +**See** + +---- + +Refer to Orafce Docmentation for information on features implemented with different external specifications in PostgreSQL and orafce. + +---- + + +### 1.2 Example Databases +This section uses the examples of an inventory management database for a particular retail store, and a staff management database for a particular company. +The explanations of data operation provided in this manual are based on these example databases. + +#### 1.2.1 Inventory Management Database +##### 1.2.1.1 Contents of Database Definitions +This database manages inventory in a retail store. +It consists of the following three tables: + + - inventory_table + - This table contains information on the products being handled and their inventory quantities. + + - ordering_table + - This table contains information on the products supplied by each supplier, their ordering quantities, and their purchase prices. + + - company_table + - This table contains the company name, telephone number, and address of each supply company. + +**inventory_table** + +The inventory table consists of the following four columns: + + - i_number (product number) + - Code assigned to the product + - i_name (category) + - Category of the product + - i_quantity (inventory quantity) + - Inventory quantity of the product + - i_warehouse (warehouse number) + - Code of the warehouse where the product is stored + +The contents of the inventory table are shown in "inventory_table". + + - ordering_table (ordering table) + - The ordering table consists of the following five columns: + - o_code (supplier) + - Company code of the supplier + - o_number (supplied product) + - Code assigned to the product + - o_price (purchase price) + - Purchase price of the product + - o_quantity (ordering quantity) + - Number of products ordered + - o_order_date (order date) + - Product order date + +The contents of the ordering table are shown in "ordering_table". + + - company_table (company table) + - The company table consists of the following four columns: + - c_code (company code) + - Code assigned to the company + - c_name (company name) + - Name of the company + - c_telephone (telephone number) + - Telephone number of the company + - c_address (address) + - Address of the company + +The contents of the company table are shown in "company_table". + +Where the table names and column names described above are used in this guide, such as in example SQL statements, unless otherwise stated, the tables and columns listed in "Contents of the inventory management database" are specified. Note that the data shown in this table is fictitious. + +**Contents of the inventory management database** + +a) inventory_table + + +| i_number
(product number) | i_name
(category) | i_quantity
(inventory quantity) | i_warehouse
(warehouse number) | +| :---: | :--- | :---: | :---: +|SMALLINT
PRIMARY KEY|VARCHAR(20)
NOT NULL|INTEGER |SMALLINT | +| 110 | television | 85 | 2 | +| 111 | television | 90 | 2 | +| 123 | refrigerator | 60 | 1 | +| 124 | refrigerator | 75 | 1 | +| 140 | cd player | 120 | 2 | +| 212 | television | 0 | 2 | +| 215 | video | 5 | 2 | +| 226 | refrigerator | 8 | 1 | +| 227 | refrigerator | 15 | 1 | +| 240 | cd player | 25 | 2 | +| 243 | cd player | 14 | 2 | +| 351 | cd | 2500 | 2 | + + +b) ordering_table + +| o_code
(supplier) | o_number
(supplied product) | o_price
(purchase price) | o_quantity
(ordering quantity) | o_order_date
(order date) | +| :---: | :---: | :---: | :---: | :---: | +| SMALLINT
NOT NULL | SMALLINT
NOT NULL | INTEGER | SMALLINT | DATE | +| 61 | 123 | 48000 | 60 | 42557 | +| 61 | 124 | 64000 | 40 | 42557 | +| 61 | 140 | 8000 | 80 | 42557 | +| 61 | 215 | 240000 | 10 | 42557 | +| 61 | 240 | 80000 | 20 | 42557 | +| 62 | 110 | 37500 | 120 | 42557 | +| 62 | 226 | 112500 | 20 | 42557 | +| 62 | 351 | 375 | 800 | 42557 | +| 63 | 111 | 57400 | 80 | 42557 | +| 63 | 212 | 205000 | 30 | 42557 | +| 63 | 215 | 246000 | 10 | 42557 | +| 71 | 140 | 7800 | 50 | 42557 | +| 71 | 351 | 390 | 600 | 42557 | +| 72 | 140 | 7000 | 70 | 42557 | +| 72 | 215 | 210000 | 10 | 42557 | +| 72 | 226 | 105000 | 20 | 42557 | +| 72 | 243 | 84000 | 10 | 42557 | +| 72 | 351 | 350 | 1000 | 42557 | +| 74 | 110 | 39000 | 120 | 42557 | +| 74 | 111 | 54000 | 120 | 42557 | +| 74 | 226 | 117000 | 20 | 42557 | +| 74 | 227 | 140400 | 10 | 42557 | +| 74 | 351 | 390 | 700 | 42557 | + + +c) company_table + +| c_code
(company code) | c_name
(company name) | c_telephone
(telephone number) | c_address
(address) | +| :---: | :--- | :---: | :---| +| SMALLINT
NOT NULL | VARCHAR(20)
NOT NULL | VARCHAR(12) | VARCHAR(50)| +| 61 | Adam Electric | 111-777-4444 | 7-8-9, Shin-Kamata, Oda Ward, Tokyo| +| 62 | Idea Corporation | 222-888-5555 | 1-2-3, Asahi Ward, Obama City, Kanagawa| +| 63 | Fullmoon Industry | 333-999-6666 | 1-1-1, Osaki, Urawa Town, Saitama| +| 71 | Stream Electric | 444-111-7777 | 4-5-6, Akasakadai, Sakaida City, Osaka| +| 72 | Tornado Industry | 555-222-8888 | 2-3-7, Higashi-Yodogawa, Nada Town, Osaka| +| 74 | First Corporation | 666-333-9999 | 4-16-16, Naka City, Kyoto| + + + +##### 1.2.1.2 Relationship Between the Tables +"Relationship between the tables" shows how the tables are related with one another. +The inventory table and the ordering table are related by means of the product number and the supplied product. +The ordering table and the company table are related by means of the supplier and the company code. For example, the product identified by the product number "123" in the inventory table has the category "refrigerator" and the inventory quantity "60", and is stored in the warehouse identified by the code "1". +Then, the row containing the supplied product "123" in the ordering table shows that the purchase price of the product is "48000" and the ordering quantity is ""60". +Furthermore, the company code of the supplier can be confirmed as "61", and the row containing the company code "61" in the company table shows the name, telephone number, and address of the company that supplies the product. + +**Relationship between the tables** + +![REL1](gif/Inventory_Management_Database.gif) + +##### 1.2.1.3 Example Database Definitions + +The following example shows table definitions for the inventory management database. + +~~~ +CREATE TABLE inventory_table ( +i_number SMALLINT PRIMARY KEY, +i_name VARCHAR(20) NOT NULL, +i_quantity INTEGER, +i_warehouse SMALLINT +); + +CREATE TABLE ordering_table ( +o_code SMALLINT NOT NULL, +o_number SMALLINT NOT NULL, +o_price INTEGER, +o_quantity SMALLINT, +o_order_date DATE +); + +CREATE TABLE company_table ( +c_code SMALLINT NOT NULL, +c_name VARCHAR(20) NOT NULL, +c_telephone VARCHAR(12), +c_address VARCHAR(50) +); + +INSERT INTO inventory_table VALUES (110, 'television', 85, 2); +INSERT INTO inventory_table VALUES (111, 'television', 90, 2); +INSERT INTO inventory_table VALUES (123, 'refrigerator', 60, 1); +INSERT INTO inventory_table VALUES (124, 'refrigerator', 75, 1); +INSERT INTO inventory_table VALUES (140, 'cd player', 120, 2); +INSERT INTO inventory_table VALUES (212, 'television', 0, 2); +INSERT INTO inventory_table VALUES (215, 'video', 5, 2); +INSERT INTO inventory_table VALUES (226, 'refrigerator', 8, 1); +INSERT INTO inventory_table VALUES (227, 'refrigerator', 15, 1); +INSERT INTO inventory_table VALUES (240, 'cd player', 25, 2); +INSERT INTO inventory_table VALUES (243, 'cd player', 14, 2); +INSERT INTO inventory_table VALUES (351, 'cd', 2500, 2); + +INSERT INTO ordering_table VALUES (61, 123, 48000, 60, DATE'2016-07-06'); +INSERT INTO ordering_table VALUES (61, 124, 64000, 40, DATE'2016-07-06'); +INSERT INTO ordering_table VALUES (61, 140, 8000, 80, DATE'2016-07-06'); +INSERT INTO ordering_table VALUES (61, 215, 240000, 10, DATE'2016-07-06'); +INSERT INTO ordering_table VALUES (61, 240, 80000, 20, DATE'2016-07-06'); +INSERT INTO ordering_table VALUES (62, 110, 37500, 120, DATE'2016-07-06'); +INSERT INTO ordering_table VALUES (62, 226, 112500, 20, DATE'2016-07-06'); +INSERT INTO ordering_table VALUES (62, 351, 375, 800, DATE'2016-07-06'); +INSERT INTO ordering_table VALUES (63, 111, 57400, 80, DATE'2016-07-06'); +INSERT INTO ordering_table VALUES (63, 212, 205000, 30, DATE'2016-07-06'); +INSERT INTO ordering_table VALUES (63, 215, 246000, 10, DATE'2016-07-06'); +INSERT INTO ordering_table VALUES (71, 140, 7800, 50, DATE'2016-07-06'); +INSERT INTO ordering_table VALUES (71, 351, 390, 600, DATE'2016-07-06'); +INSERT INTO ordering_table VALUES (72, 140, 7000, 70, DATE'2016-07-06'); +INSERT INTO ordering_table VALUES (72, 215, 210000, 10, DATE'2016-07-06'); +INSERT INTO ordering_table VALUES (72, 226, 105000, 20, DATE'2016-07-06'); +INSERT INTO ordering_table VALUES (72, 243, 84000, 10, DATE'2016-07-06'); +INSERT INTO ordering_table VALUES (72, 351, 350, 1000, DATE'2016-07-06'); +INSERT INTO ordering_table VALUES (74, 110, 39000, 120, DATE'2016-07-06'); +INSERT INTO ordering_table VALUES (74, 111, 54000, 120, DATE'2016-07-06'); +INSERT INTO ordering_table VALUES (74, 226, 117000, 20, DATE'2016-07-06'); +INSERT INTO ordering_table VALUES (74, 227, 140400, 10, DATE'2016-07-06'); +INSERT INTO ordering_table VALUES (74, 351, 390, 700, DATE'2016-07-06'); + +INSERT INTO company_table +VALUES (61, 'Adam Electric', '111-777-4444', '7-8-9, Shin-Kamata, Oda Ward, Tokyo'); +INSERT INTO company_table +VALUES (62, 'Idea Corporation', '222-888-5555', '1-2-3, Asahi Ward, Obama City, Kanagawa'); +INSERT INTO company_table +VALUES (63, 'Fullmoon Industry', '333-999-6666', '1-1-1, Osaki, Urawa Town, Saitama'); +INSERT INTO company_table +VALUES (71, 'Stream Electric', '444-111-7777', '4-5-6, Akasakadai, Sakaida City, Osaka'); +INSERT INTO company_table +VALUES (72, 'Tornado Industry', '555-222-8888', '2-3-7, Higashi-Yodogawa, Nada Town, Osaka'); +INSERT INTO company_table +VALUES (74, 'First Corporation', '666-333-9999', '4-16-16, Naka City, Kyoto'); +~~~ + + +#### 1.2.2 Staff Management Database + +##### 1.2.2.1 Contents of Database Definitions + +This database manages the staff of the company. It consists of the following two tables: + + - staff_table (staff table) + - This table contains the name, position, age, and manager of each staff member. + - attendance_table (attendance management table) + - This table contains the attendance time of each staff member. + +**staff_table** + +The staff table consists of the following five columns: + + - staff_id (staff identification number) + - Code assigned to the staff member + - name (name of the staff member) + - Name of the staff member + - job (position) + - Position title of the staff member + - age (age) + - Age of the staff member + - manager_id (staff identification number of manager) + - Code assigned to the manager of the staff member + +The contents of the staff table are shown in "staff_table". + +**attendance_table** + +The attendance management table consists of the following three columns: + + - staff_id (staff identification number) + - Code assigned to the staff member + - work_flag (attendance flag) + - Flag indicating whether the staff member is present or absent + - attendance_time (attendance time) + - Work starting and ending times of the staff member + +The contents of the attendance management table are shown in "attendance_table". + +Where the table names and column names described above are used in this guide, such as in example SQL statements, unless otherwise stated, the tables and columns listed in "Contents of the staff management database" are specified. Note that the data shown in this table is fictitious. + +**Contents of the staff management database** + +a) staff_table + +| staff_id
(staff ID number) | name
(name of the staff member) | job
(position) | age
(age) | manager_id
(staff ID number of manager) | +|:---:|:---|:---|:---:|:---:| +|CHAR(4)|VARCHAR(20)|VARCHAR(30)|INTEGER|CHAR(4)| +| 1001 | tokyo taro | president | 68 |\| +| 2001 | oosaka jiro | sales manager | 48 | 1001 | +| 3001 | hyogo akihiko | sales member | 28 | 2001 | +| 3002 | mie megumi | sales member | 31 | 2001 | +| 3003 | hirosima taro | sales member | 36 | 2001 | +| 3004 | nagano motoko | sales member | 40 | 2001 | +| 3005 | akita taro | sales member | 25 | 2001 | +| 2002 | hukuoka saburo | accounting manager | 52 | 1001 | +| 4001 | nagasaki rokuro | accounting member | 39 | 2002 | +| 2003 | kyoto hanako | general affairs manager | 43 | 1001 | +| 5001 | okayama reiko | general affairs member | 33 | 2003 | +| 5002 | kagawa shiro | general affairs member | 27 | 2003 | +| 5003 | okinawa takao | general affairs member | 30 | 2003 | +| 5004 | miyagi kenta | \ | 23 | 2003 | +| 5005 | aichi yui | ''(null) | 23 | 2003 | + + + + + + + +b) attendance_table + +| staff_id
(staff ID number) | work_flag
(attendance flag) | attendance_time
(attendance time) | +| :--- | :--- | :--- | +| CHAR(4) | CHAR(1) | TIMESTAMP WITH TIME ZONE | +| 1001 | i | 2016-07-06 08:00:00+09 | +| 3001 | i | 2016-07-06 08:30:00+09 | +| 1001 | o | 2016-07-06 17:30:00+09 | +| 3001 | o | 2016-07-06 18:00:00+09 | + + +##### 1.2.2.2 Relationship Between the Tables +"Relationship between the tables" shows how the tables are related with one another. The staff table and the attendance management table are related by means of the staff identification number. + +**Relationship between the tables** + +![REL2](gif/Staff_Management_Database.gif) + +##### 1.2.2.3 Example Database Definitions +The following example shows table definitions for the staff management database. + +~~~ +CREATE TABLE staff_table ( +staff_id CHAR(4), +name VARCHAR(20), +job VARCHAR(30), +age INTEGER, +manager_id CHAR(4) +); + +CREATE TABLE attendance_table ( +staff_id CHAR(4), +work_flag CHAR(1), +attendance_time TIMESTAMP WITH TIME ZONE +); + +INSERT INTO staff_table VALUES ('1001', 'tokyo taro', 'president', 68, NULL); +INSERT INTO staff_table VALUES ('2001', 'oosaka jiro', 'sales manager', 48, '1001'); +INSERT INTO staff_table VALUES ('3001', 'hyogo akihiko', 'sales member', 28, '2001'); +INSERT INTO staff_table VALUES ('3002', 'mie megumi', 'sales member', 31, '2001'); +INSERT INTO staff_table VALUES ('3003', 'hirosima taro', 'sales member', 36, '2001'); +INSERT INTO staff_table VALUES ('3004', 'nagano motoko', 'sales member', 40, '2001'); +INSERT INTO staff_table VALUES ('3005', 'akita taro', 'sales member', 25, '2001'); +INSERT INTO staff_table VALUES ('2002', 'hukuoka saburo', 'accounting manager', 52, '1001'); +INSERT INTO staff_table VALUES ('4001', 'nagasaki rokuro', 'accounting member', 39, '2002'); +INSERT INTO staff_table VALUES ('2003', 'kyoto hanako', 'general affairs manager', 43, '1001'); +INSERT INTO staff_table VALUES ('5001', 'okayama reiko', 'general affairs member', 33, '2003'); +INSERT INTO staff_table VALUES ('5002', 'kagawa shiro', 'general affairs member', 27, '2003'); +INSERT INTO staff_table VALUES ('5003', 'okinawa takao', 'general affairs member', 30, '2003'); +INSERT INTO staff_table VALUES ('5004', 'miyagi kenta', NULL, 23, '2003'); +INSERT INTO staff_table VALUES ('5005', 'aichi yui', '', 23, '2003'); + +INSERT INTO attendance_table VALUES ('1001', 'i', TIMESTAMP WITH TIME ZONE'2016-07-06 08:00:00+09:00'); +INSERT INTO attendance_table VALUES ('3001', 'i', TIMESTAMP WITH TIME ZONE'2016-07-06 08:30:00+09:00'); +INSERT INTO attendance_table VALUES ('1001', 'o', TIMESTAMP WITH TIME ZONE'2016-07-06 17:30:00+09:00'); +INSERT INTO attendance_table VALUES ('3001', 'o', TIMESTAMP WITH TIME ZONE'2016-07-06 18:00:00+09:00'); +~~~ \ No newline at end of file diff --git a/contrib/orafce/doc/sql_migration/sql_migration02.md b/contrib/orafce/doc/sql_migration/sql_migration02.md new file mode 100644 index 000000000..b6e0cd9bc --- /dev/null +++ b/contrib/orafce/doc/sql_migration/sql_migration02.md @@ -0,0 +1,759 @@ +Chapter 2 Migrating Syntax Elements +--- + +This chapter explains how to migrate SQL syntax elements. + +### 2.1 Basic Elements + +This section explains the basic elements of SQL syntax. + +#### 2.1.1 TIMESTAMP Literal + +**Description** + +A literal with the TIMESTAMP prefix is treated as TIMESTAMP type data. + +**Functional differences** + + - **Oracle database** + - The TIMESTAMP prefix can signify a time zone literal. + - **PostgreSQL** + - The TIMESTAMP WITH TIME ZONE prefix signifies a time zone literal. If the prefix is TIMESTAMP only, the time zone value is discarded. No warning, error, or other notification is output. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword TIMESTAMP and identify where it is used as a literal prefix. + 2. If a time zone has been specified for the literal, change the prefix to TIMESTAMP WITH TIME ZONE. + +**Migration example** + +The example below shows how to migrate a TIMESTAMP literal with time zone. + + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
INSERT INTO attendance_table 
+ VALUES( '1001', 
+ 'i', 
+ TIMESTAMP'2016-05-20 12:30:00 +09:00' );
+
+
INSERT INTO attendance_table 
+ VALUES( '1001',
+         'i',
+ TIMESTAMP WITH TIME ZONE'2016-05-20 12:30:00 +09:00' );
+
+ +#### 2.1.2 Alternate Quotation Literal + +**Description** + +Using alternate quotation enables a delimiter to be used for a text string. + +**Functional differences** + + - **Oracle database** + - The alternate quotation marks q'x and x' are used. The letter x represents the alternate character. + - **PostgreSQL** + - The alternate quotation marks $q$ and $q$ are used. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword q' or Q' and identify where alternate quotation marks are used. + 2. Delete alternate characters and single quotation marks where alternate quotation has been used, and enclose strings with $q$. The character between the two $ symbols can be omitted or any string can be specified. + +**Migration example** + +The example below shows how to migrate alternate quotation. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SELECT q'[Adam Electric company's address]' 
+ FROM DUAL;
+
+
SELECT $q$Adam Electric company's address$q$ 
+ FROM DUAL;
+
+ + +**See** + +---- + +Refer to "The SQL Language" > "Lexical Structure" > "Dollar-quoted String Constants" in the PostgreSQL Documentation for information on alternate quotation marks. + +---- + +### 2.2 Data Types + +This section explains data types. + +#### 2.2.1 Migrating Data Types + +The table below lists the PostgreSQL data types that correspond to Oracle database data types. + +**Data type correspondence** + + - **Character** + +|Oracle database Data type |Remarks |Migratability |PostgreSQL Data type |Remarks | +| :--- | :--- | :---: | :--- | :--- | +| CHAR
CHARACTER | Specifies the number of bytes or number of characters. | YR | char
character | Only the number of characters can be specified. | +| CLOB
CHAR LARGE OBJECT
CHARACTER LARGE OBJECT | | MR | text
Large object | Up to 1 GB(text)
Up to 4 TB(Large object) | +| CHAR VARYING
CHARACTER VARYING
VARCHAR | Specifies the number of bytes or number of characters. | YR | char varying
character varying
varchar | Only the number of characters can be specified. LONG MR text Up to 1 GB M Large object +| NCHAR
NATIONAL CHAR
NATIONAL CHARACTER | | YR | nchar
national char
national character | This data type is internally used as a character type. | +| NCHAR VARYING
NATIONAL CHAR VARYING
NATIONAL CHARACTER VARYING | | YR | nchar varying
national char varying
national character varying | This data type is internally used as a character varying type | +| NCLOB
NCHAR LARGE OBJECT
NATIONAL CHARACTER LARGE OBJECT | | MR | text
Large object | Up to 1 GB(text)
Up to 4 TB(Large object) NVARCHAR2 YR nvarchar2 Collating sequence is not supported.
This data type is added using orafce. MR nchar varying
national char varying
national character varying This data type is internally used as a character varying type. VARCHAR2 Specifies the number of bytes or number of characters. YR varchar2 Only the number of bytes can be specified.
Collating sequence is not supported.
This data type is added using orafce. MR varchar Only the number of characters can be specified. + + - **Numeric** + +|Oracle database Data type |Remarks |Migratability |PostgreSQL Data type |Remarks | +| :--- | :--- | :---: | :--- | :--- | +| BINARY_DOUBLE | | M | double precision | | +| BINARY_FLOAT | | M | real | | +| DOUBLE PRECISION | | Y | double precision | | +| FLOAT | | Y | float | | +| INT
INTEGER | | Y | int
integer | | +| NUMBER
DEC
DECIMAL
NUMERIC | Specifies numbers rounded according to the scale definition. | MR | smallint
integer
bigint
numeric | Integers from -32,768 to +32,767 (smallint)
Integers from -2,147,483,648 to +2,147,483,647 (integer)
Integers from -9,223,372,036,854,775,808 to +9,223,372,036,854,775,807(bigint) | +| REAL | | Y | real | | +| SMALLINT | | Y | smallint | | + + - **Date and time** + +|Oracle database Data type |Remarks |Migratability |PostgreSQL Data type |Remarks | +| :--- | :--- | :---: | :--- | :--- | +| INTERVAL DAY TO SECOND | | Y | interval day to second | | +| INTERVAL YEAR TO MONTH | | Y | interval year to month | | +| TIMESTAMP | | Y | timestamp | | +| TIMESTAMP WITH LOCAL TIME ZONE | | M | timestamp with time zone | | +| TIMESTAMP WITH TIME ZONE | | Y | timestamp with time zone | DATE Y date (orafce) The time can be stored in addition to the date.
The search_path parameter must be specified. YR date (PostgreSQL) Only the date is stored. M timestamp + + - **Binary** + +|Oracle database Data type |Remarks |Migratability |PostgreSQL Data type |Remarks | +| :--- | :--- | :---: | :--- | :--- | +| BFILE | | MR | bytea
Large object | Up to 1 GB (bytea)
Up to 4 TB(Large object) | +| BLOB
BINARY LARGE OBJECT | | MR | bytea
Large object | Up to 1 GB (bytea)
Up to 4 TB(Large object) | +| LONG RAW | | MR | bytea
Large object | Up to 1 GB (bytea)
Up to 4 TB(Large object) | +| RAW | | M | bytea | | +| ROWID | | M | oid | +| UROWID | | N | | | + + - **Other** + +|Oracle database Data type |Remarks |Migratability |PostgreSQL Data type |Remarks | +| :--- | :--- | :---: | :--- | :--- | +| ANYDATA | | N | | | +| ANYDATASET | | N | | | +| ANYTYPE | | N | | | +| DBUriType | | N | | | +| HTTPUriType | | N | | | +| MLSLABEL | | N | | | +| ORDAudio | | N | | | +| ORDDicom | | N | | | +| ORDDoc | | N | | | +| ORDImage | | N | | | +| ORDVideo | | N | | | +| REF data type | | N | | | +| SDO_GEOMETRY | | N | | | +| SDO_GEORASTER | | N | | | +| SDO_TOPO_GEOMETRY | | N | | | +| SI_AverageColor | | N | | | +| SI_Color | | N | | | +| SI_ColorHistogram | | N | | | +| SI_FeatureList | | N | | | +| SI_PositionalColor | | N | | | +| SI_StillImage | | N | | | +| SI_Texture | | N | | | +| URIFactory package | | N | | | +| URIType | | N | | | +| VARRAY | | M | Array type | | +| XDBUriType | | N | | | +| XMLType | | M | XML type | | +| Object type | | N | | | +| Nested table | | N | | | + +Y: Data type can be migrated as is + +M: Modified data type can be migrated + +N: Cannot be migrated + +YR: Data type can be migrated as is with restrictions + +MR: Modified data type can be migrated with restrictions + +#### 2.2.2 Examples of Migrating Data Types + +##### 2.2.2.1 Examples of Migrating General Data Types + +**Description of migration** + +Refer to "Data type correspondence" and change Oracle database data types to PostgreSQL data types. + +**Migration example** + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
CREATE TABLE t1( col1 SMALLINT, 
+                 col2 VARCHAR2(10), 
+                 col3 NVARCHAR2(10), 
+                 col4 DATE, 
+                 col5 NUMBER(10), 
+                 col6 RAW(2000), 
+                 col7 BLOB ); 
+
+
CREATE TABLE t1( col1 SMALLINT, 
+                 col2 VARCHAR(10), 
+                 col3 NCHAR VARYING(10), 
+                 col4 TIMESTAMP, 
+                 col5 INTEGER, 
+                 col6 BYTEA, 
+                 col7 BYTEA );
+
+
+ +##### 2.2.2.2 NUMBER Type + +**Functional differences** + + - **Oracle database** + - A negative value can be specified in a NUMBER type scale.
Any value that is specified in a scale beyond the number of significant digits is rounded off. + - **PostgreSQL** + - A negative value cannot be specified in a NUMERIC type scale.
Any value that is specified in a scale beyond the number of significant digits is discarded. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Change DECIMAL scales to 0, and add the number of changed digits to the precision. + 2. Create a function that uses the ROUND function to round off the column that was changed in Step (1) above. + 3. Create a trigger that executes the function created in Step (2) above when the INSERT statement and UPDATE statement are executed. + + +**Migration example** + +The example below shows how to migrate the NUMBER type. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
CREATE TABLE t1( col1 SMALLINT, 
+                 col2 NUMBER(10,-2) ); 
+
+
+
+
+
+
+
+
+
+
+
+ INSERT INTO t1 VALUES( 11, 1234567890 ); + SELECT * FROM t1;
+
+
 CREATE TABLE t1( col1 SMALLINT, 
+                  col2 NUMERIC(12,0) ); 
+
+ CREATE FUNCTION f1() RETURNS TRIGGER AS $$ + BEGIN + NEW.col2 := ROUND(NEW.col2,-2); + RETURN NEW; + END; + $$ LANGUAGE plpgsql; + CREATE TRIGGER g1 BEFORE INSERT OR UPDATE ON t1 + FOR EACH ROW + EXECUTE PROCEDURE f1(); +
+ INSERT INTO t1 VALUES( 11, 1234567890 ); + SELECT * FROM t1; +
+
+ + + +### 2.3 Pseudocolumns + +This section explains pseudocolumns. + +#### 2.3.1 CURRVAL + +**Description** + +CURRVAL returns the value nearest to that obtained by NEXTVAL from the sequence in the current session. + +**Functional differences** + + - **Oracle database** + - The sequence name is specified as sequenceName.CURRVAL. + - **PostgreSQL** + - The sequence name is specified as CURRVAL('sequenceName'). + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword CURRVAL and identify where it is used. + 2. Change the sequence name specification by placing it after CURRVAL and enclosing it in parentheses and single quotation marks. + +**Migration example** + +The example below shows how to migrate CURRVAL. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SELECT seq1.CURRVAL FROM DUAL;
+
+
SELECT CURRVAL('seq1') FROM DUAL;
+
+
+ + +#### 2.3.2 NEXTVAL + +**Description** + +NEXTVAL returns the next number in the sequence. + +**Functional differences** + + - **Oracle database** + - The sequence name is specified as sequenceName.NEXTVAL. + - **PostgreSQL** + - The sequence name is specified as NEXTVAL('sequenceName'). + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword NEXTVAL and identify where it is used. + 2. Change the sequence name specification by placing it after NEXTVAL and enclosing it in parentheses and single quotation marks. + +**Migration example** + +The example below shows how to migrate NEXTVAL. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SELECT seq1.NEXTVAL FROM DUAL;
+
+
SELECT NEXTVAL('seq1') FROM DUAL;
+
+
+ + +#### 2.3.3 ROWID + +**Description** + +ROWID obtains information for uniquely identifying data. + +**Functional differences** + + - **Oracle database** + - ROWID is created automatically when a table is created. + - **PostgreSQL** + - ROWID cannot be used. Use OID instead. However, WITH OIDS must be specified when a table is created. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Specify WITH OIDS at the end of the CREATE TABLE statement. + 2. Change the ROWID extraction item in the SELECT statement to OID. + +**Migration example** + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 CREATE TABLE t1( col1 INTEGER ); 
+
+ INSERT INTO t1 VALUES( 11 ); + SELECT ROWID, col1 FROM t1;
+
+
 CREATE TABLE t1( col1 INTEGER ) WITH OIDS; 
+
+ INSERT INTO t1 VALUES( 11 ); + SELECT OID, col1 FROM t1; +
+
+ + +#### 2.3.4 ROWNUM + +**Description** + +ROWNUM obtains the number of the current row. + +##### 2.3.4.1 Obtaining the Row Number + +**Functional differences** + + - **Oracle database** + - ROWNUM obtains the number of the current row. + - **PostgreSQL** + - ROWNUM cannot be used. Use ROW_NUMBER() OVER() instead. + +**Migration procedure** + +Using the ROW_NUMBER() function instead of ROWNUM, perform migration so that the current number is obtained. Use the following procedure to perform migration: + + 1. Search for the keyword ROWNUM and identify where it is used. + 2. Change ROWNUM to ROW_NUMBER() OVER(). + +**Migration example** + +The example below shows migration when a line number is obtained. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SELECT ROWNUM, i_number, i_name 
+ FROM inventory_table;
+
+
SELECT ROW_NUMBER() OVER(), i_number, i_name 
+ FROM inventory_table; 
+
+ + +**Note** + +---- + +This migration example cannot be used with the UPDATE statement. + +---- + +##### 2.3.4.2 Sorting Records and Obtaining the First N Records + +**Functional differences** + + - **Oracle database** + - If a subquery that contains an ORDER BY clause is specified in the FROM clause and a ROWNUM condition is defined in the WHERE clause, the records are sorted and then the first N records are obtained. + - **PostgreSQL** + - ROWNUM cannot be used. Using the LIMIT clause instead, sort the records and obtain the first N records. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword ROWNUM and identify where it is used. + 2. If an ORDER BY clause is specified in a subquery of the FROM clause and the results are filtered according to the ROWNUM condition in the WHERE clause, regard this portion as an SQL statement that sorts the records and then obtains the first N records. + 3. Move the table name and ORDER BY clause from the FROM clause subquery to a higher SELECT statement and delete the subquery. + 4. In the LIMIT clause, set the same number as the ROWNUM condition of the WHERE clause, and delete the ROWNUM condition from the WHERE clause. + +**Migration example** + +The example below shows migration when records are sorted and then the first N records are obtained. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SELECT i_number, i_name 
+ FROM ( SELECT * FROM inventory_table 
+ ORDER BY i_number DESC ) 
+ WHERE ROWNUM < 5;
+
+
SELECT i_number, i_name  
+ FROM inventory_table 
+ ORDER BY i_number DESC LIMIT 4; 
+
+
+ + +### 2.4 Treatment of NULL and Zero-Length Strings + +This section explains how NULL and zero-length strings are treated. + +Oracle databases treat zero-length strings as NULL. In contrast, PostgreSQL treats zero-length strings and NULL as two different values. + +The table below lists the advantages and disadvantages of using zero-length strings and NULL when performing migration. + + +**Advantages and disadvantages of retaining or migrating Oracle database zero-length strings** + +|Oracle database zero-length strings |Advantages |Disadvantages | +|:---|:---|:---| +|Treated as zero-length strings
without being migrated to NULL | String concatenation (||) can be used as is. | The target data has fewer hits than with IS NULL.
Conditional expressions must be changed. | +| Migrated to NULL | IS NULL can be used as is. | The result of string concatenation (||) is NULL.
String concatenation must be changed. | + +The following sections explain how to make changes if zero-length strings and NULL values are used together. + + +#### 2.4.1 Search Conditions (IS NULL Predicate) + +**Functional differences** + + - **Oracle database** + - Even zero-length strings hit the IS NULL condition. + - **PostgreSQL** + - Zero-length strings do not hit the IS NULL condition. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword IS NULL and identify where a NULL search is used. + 2. Change the portions found by the IS NULL search to IS NULL OR strName = ''. + +**Migration example** + +The example below shows migration when a search for zero-length strings and NULL values is performed. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SELECT * FROM staff_table 
+ WHERE job IS NULL;
+
+
SELECT * FROM staff_table 
+ WHERE job IS NULL OR job = '';
+
+ + +The example below shows migration when a search for values other than zero-length strings and NULL values is performed. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SELECT * FROM staff_table 
+ WHERE job IS NOT NULL;
+
+
SELECT * FROM staff_table 
+ WHERE job IS NOT NULL AND job != '';
+
+
+ + + +#### 2.4.2 Search Conditions (Comparison Predicate) + +**Functional differences** + + - **Oracle database** + - Zero-length strings are treated as NULL, so they do not match search conditions. + - **PostgreSQL** + - Zero-length strings are not treated as NULL, so they can match search conditions. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the name of the column where the zero-length string is stored, and identify where a string comparison is used. + 2. Add AND columnName != '' to the search condition. + +**Migration example** + +The example below shows migration when a zero-length string comparison is specified as the search condition. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SELECT * FROM staff_table 
+ WHERE job < 'A00';
+
+
SELECT * FROM staff_table 
+ WHERE job < 'A00' AND job != '';
+
+
+ + +#### 2.4.3 String Concatenation (||) + +**Functional differences** + + - **Oracle database** + - Concatenation with NULL returns strings other than NULL. + - **PostgreSQL** + - Concatenation with NULL returns NULL. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword || and identify where string concatenation is used. + 2. If the values to be concatenated are likely to become NULL, use the NVL function to return a zero-length string instead of NULL. + +**Migration example** + +The example below shows migration when NULL is returned by string concatenation (||) in Oracle databases. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SELECT 'NAME:' || name 
+ FROM staff_table;
+
+
 SELECT 'NAME:' || NVL( name, '' ) 
+  FROM staff_table;
+
+
+ + diff --git a/contrib/orafce/doc/sql_migration/sql_migration03.md b/contrib/orafce/doc/sql_migration/sql_migration03.md new file mode 100644 index 000000000..ade1b45c1 --- /dev/null +++ b/contrib/orafce/doc/sql_migration/sql_migration03.md @@ -0,0 +1,493 @@ +Chapter 3 Migrating Functions +--- + +This chapter explains how to migrate SQL functions. + +### 3.1 CONVERT + +**Description** + +CONVERT converts a string from one character set to another. + +**Functional differences** + + - **Oracle database** + - The string is converted from the character set identified in the third argument to the character set identified in the second argument. + - **PostgreSQL** + - The string is converted from the character set identified in the second argument to the character set identified in the third argument. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword CONVERT and identify where it is used. + 2. Switch the second and third arguments. + 3. Change the character sets in the second and third arguments to names that are valid under the PostgreSQL encoding system. + +**Migration example** + +The example below shows migration when strings are changed to the character set of the target database. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SELECT CONVERT( 'abc', 
+ 'JA16EUC', 
+ 'AL32UTF8' ) 
+ FROM DUAL;
+
+
SELECT CONVERT( CAST( 'abc' AS BYTEA ), 
+  'UTF8', 
+ 'EUC_JP' ) 
+ FROM DUAL;
+
+ +### 3.2 EMPTY_BLOB + +**Description** + +EMPTY_BLOB initializes BLOB type areas and creates empty data. + +**Functional differences** + + - **Oracle database** + - BLOB type areas are initialized and empty data is created. + - **PostgreSQL** + - EMPTY_BLOB cannot be used. Instead, use a zero-length string for initialization. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword EMPTY_BLOB() and identify where it is used. + 2. Change EMPTY_BLOB() to the zero-length string ''. + +**Migration example** + +The example below shows migration when empty data is inserted into the BLOB column. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
CREATE TABLE t1( col1 INTEGER, 
+ col2 BLOB ); 
+
+ INSERT INTO t1 VALUES( 11, EMPTY_BLOB() );
+
+
CREATE TABLE t1( col1 INTEGER, 
+ col2 BYTEA ); 
+
+ INSERT INTO t1 VALUES( 11, '' );
+
+ + +The example below shows migration when BLOB column data is updated to empty. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
UPDATE t1 SET col2 = EMPTY_BLOB() WHERE col1 = 11;
+
+
UPDATE t1 SET col2 = '' WHERE col1 = 11;
+
+ +### 3.3 LEAD + +**Description** + +LEAD obtains the value of the column specified in the arguments from the record that is the specified number of lines below. + +**Functional differences** + + - **Oracle database** + - A NULL value in the column specified in the arguments can be excluded from the calculation. + - **PostgreSQL** + - A NULL value in the column specified in the arguments cannot be excluded from the calculation. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword LEAD and identify where it is used. + 2. If the IGNORE NULLS clause is specified, check the following values to create a subquery that excludes NULL values: + - Arguments of LEAD (before IGNORE NULLS) + - Tables targeted by IGNORE NULLS + - Columns targeted by IGNORE NULLS + - Columns to be sorted + 3. Change the table in the FROM clause to a subquery to match the format shown below. + 4. Replace LEAD in the select list with MAX. Specify LEAD_IGNLS in the arguments of MAX, and PARTITION BY CNT in the OVER clause. + +~~~ +FROM ( SELECT columnBeingUsed, + CASE + WHEN ignoreNullsTargetColumn IS NOT NULL THEN + LEAD( leadFunctionArguments ) + OVER( PARTITION BY NVL2( ignoreNullsTargetColumn, '0', '1' ) + ORDER BY sortTargetColumn ) + END AS LEAD_IGNLS, + COUNT( ignoreNullsTargetColumn ) + OVER( ORDER BY sortTargetColumn + ROWS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING ) + AS CNT + FROM ignoreNullsTargetTable ) AS T1; +~~~ + +**Migration example** + +The example below shows migration when NULL values are not included in the calculation of column values. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SELECT staff_id, 
+       name, 
+       job, 
+ LEAD( job, 1 ) IGNORE NULLS 
+ OVER( ORDER BY staff_id DESC) 
+ AS "LEAD_IGNORE_NULLS" 
+ FROM staff_table 
+ ORDER BY staff_id DESC;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
SELECT staff_id, 
+       name, 
+       job, 
+ MAX( LEAD_IGNLS ) 
+ OVER( PARTITION BY CNT ) 
+ AS "LEAD_IGNORE_NULLS" 
+ FROM ( SELECT staff_id, 
+       name, 
+       job, 
+ CASE 
+ WHEN job IS NOT NULL THEN 
+ LEAD( job, 1 ) 
+ OVER( PARTITION BY NVL2( 
+       job, 
+       '0', 
+       '1' ) 
+ ORDER BY staff_id DESC) 
+ END AS LEAD_IGNLS, 
+ COUNT( job ) 
+ OVER( ORDER BY staff_id DESC 
+  ROWS BETWEEN 1 FOLLOWING 
+  AND UNBOUNDED FOLLOWING ) 
+ AS CNT 
+ FROM staff_table ) AS T1 
+ ORDER BY staff_id DESC;
+
+ +**Information** + +---- + +If the IGNORE NULLS clause is not specified or if the RESPECT NULLS clause is specified, NULL is included in the calculation, so operation is the same as the LEAD function of PostgreSQL. Therefore, if the IGNORE NULLS clause is not specified, no changes need to be made. If the RESPECT NULLS clause is specified, delete the RESPECT NULLS clause. + +The example below shows migration when RESPECT NULLS is specified in the Oracle database. + +**LEAD migration example (when RESPECT NULLS is specified)** + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SELECT staff_id, 
+       name, 
+       job, 
+ LEAD( job, 1 ) 
+ RESPECT NULLS 
+ OVER( ORDER BY staff_id DESC ) 
+ AS "LEAD_RESPECT_NULLS" 
+ FROM staff_table 
+ ORDER BY staff_id DESC;
+
+
SELECT staff_id, 
+       name, 
+       job, 
+ LEAD( job, 1 ) 
+ OVER( ORDER BY staff_id DESC ) 
+ AS "LEAD_RESPECT_NULLS" 
+ FROM staff_table 
+ ORDER BY staff_id DESC; 
+ 
+
+ +---- + +### 3.4 RAWTOHEX + +**Description** + +RAWTOHEX converts RAW type data to a hexadecimal string value. + +**Functional differences** + + - **Oracle database** + - RAW type data is converted to a hexadecimal string value. + - **PostgreSQL** + - RAWTOHEX cannot be used. Instead, use ENCODE to convert binary data types corresponding to the RAW type. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword RAWTOHEX and identify where it is used. + 2. Change RAWTOHEX to ENCODE and specify HEX in the second argument. + +**Migration example** + +The example below shows migration when RAW data types are converted to hexadecimal string values. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SELECT RAWTOHEX ( 'ABC' ) FROM DUAL;
+
+
SELECT ENCODE ( 'ABC', 'HEX' ) FROM DUAL;
+
+ +**Information** + +---- + +A RAWTOHEX function that is used in PL/SQL to take a string as an argument must first be converted to a binary data type using DECODE, and then ENCODE must be used to convert the value to a string. + +The example below shows migration of RAWTOHEX when it is used in PL/SQL to take a string as an argument. + +**ROWTOHEX migration example (when taking a string as an argument in PL/SQL)** + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SET SERVEROUTPUT ON; 
+
+ DECLARE + HEX_TEXT VARCHAR2( 100 ); + BEGIN + HEX_TEXT := RAWTOHEX( '414243' ); +
+
+ DBMS_OUTPUT.PUT_LINE( HEX_TEXT ); + END; + / +
+
+
 
+ DO $$ 
+ DECLARE 
+ HEX_TEXT TEXT; 
+ BEGIN 
+ HEX_TEXT := 
+ ENCODE( DECODE( '414243', 'HEX' ), 'HEX' ); 
+ PERFORM DBMS_OUTPUT.SERVEROUTPUT( TRUE ); 
+ PERFORM DBMS_OUTPUT.PUT_LINE( HEX_TEXT ); 
+ END; 
+ $$ LANGUAGE plpgsql;
+
+ +---- + +### 3.5 REGEXP_REPLACE + +**Description** + +REGEXP_REPLACE uses a regular expression pattern to replace a string. + +**Functional differences** + + - **Oracle database** + - All strings that match the regular expression pattern are replaced. + - **PostgreSQL** + - The first string that matches the regular expression pattern is replaced. + +**Migration procedure** + +The REGEXP_REPLACE function of PostgreSQL can return the same result if the option string is specified in the fourth argument. Use the following procedure to perform migration: + +1. Search for the keyword REGEXP_REPLACE and identify where it is used. + 2. Specify the argument 'g' in the fourth argument of REGEXP_REPLACE. + +**Migration example** + +The example below shows migration when a regular expression pattern is used to convert a string. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SELECT REGEXP_REPLACE( '2016', 
+       '[0-2]', 
+       '*' ) AS "REGEXP_REPLACE" 
+ FROM DUAL; 
+ 
+
+
SELECT REGEXP_REPLACE( '2016', 
+       '[0-2]', 
+       '*', 
+       'g' ) AS "REGEXP_REPLACE" 
+ FROM DUAL;
+
+ +### 3.6 TO_TIMESTAMP + +**Description** + +TO_TIMESTAMP converts a string value to the TIMESTAMP data type. + +**Functional differences** + + - **Oracle database** + - The language to be used for returning the month and day of the week can be specified. + - **PostgreSQL** + - The language to be used for returning the month and day of the week cannot be specified. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword TO_TIMESTAMP and identify where it is used. + 2. If the third argument of TO_TIMESTAMP is specified, delete it. + 3. If the a string in the first argument contains a national character string, it is replaced with a datetime keyword supported by PostgreSQL. + +**Migration example** + +The example below shows migration when a string value is converted to the TIMESTAMP data type. One string specifies the month in Japanese as a national character, so it is replaced with the date keyword 'JULY'. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SELECT TO_TIMESTAMP('2016/**/21 14:15:30', 
+        'YYYY/MONTH/DD HH24:MI:SS', 
+        'NLS_DATE_LANGUAGE = Japanese') 
+ FROM DUAL;
+
+
SELECT TO_TIMESTAMP('2016/JULY/21 14:15:30', 
+        'YYYY/MONTH/DD HH24:MI:SS') 
+
+ FROM DUAL;
+
+ +\*\*: The July in Japanese + diff --git a/contrib/orafce/doc/sql_migration/sql_migration04.md b/contrib/orafce/doc/sql_migration/sql_migration04.md new file mode 100644 index 000000000..b4745b46f --- /dev/null +++ b/contrib/orafce/doc/sql_migration/sql_migration04.md @@ -0,0 +1,1650 @@ +Chapter 4 Migrating SQL Statements +--- + +This chapter explains how to migrate SQL statements. + +### 4.1 Partitions + +This section provides examples of migrating partitions. + +#### 4.1.1 Partition Tables + +**Description** + +Partitions split tables and indexes into smaller units for management. + +The following example shows conversion of an Oracle database partition to child tables in PostgreSQL. All migration examples provided here are based on this table. + +**Example of tables created by partitioning inventory_table** + +| i_number
(product code) | i_name
(category) | i_quantity
(inventory quantity) | i_warehouse
(warehouse code) | +| :---: | :--- | :---: | :---: | +| SMALLINT
PRIMARY KEY | VARCHAR(20)
NOT NULL | INTEGER | SMALLINT | +| 123 | refrigerator | 60 | 1 | +| 124 | refrigerator | 75 | 1 | +| 226 | refrigerator | 8 | 1 | +| 227 | refrigerator | 15 | 1 | +| 110 | television | 85 | 2 | +| 111 | television | 90 | 2 | +| 140 | cd player | 120 | 2 | +| 212 | television | 0 | 2 | +| 215 | Video | 5 | 2 | +| 240 | cd player | 25 | 2 | +| 243 | cd player | 14 | 2 | +| 351 | Cd | 2500 | 2 | + + +**Functional differences** + + - **Oracle database** + - Partition tables can be created. + - **PostgreSQL** + - Partition tables cannot be created. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword PARTITION and identify where CREATE TABLE is used to create a partition. + 2. Delete the PARTITION clause and following lines from the CREATE TABLE statement and create a table. + 3. Create a child table that inherits the table defined in step 1, and add table constraints to the split table for defining partition constraints. + 4. Define a trigger or rule so that data inserted to the table is assigned to the appropriate child table. + +**Migration example** + +The example below shows migration when partitions are created in inventory_table. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
CREATE TABLE inventory_table( 
+ i_number SMALLINT PRIMARY KEY, 
+ i_name VARCHAR2(20) NOT NULL, 
+ i_quantity INTEGER, 
+ i_warehouse SMALLINT ) 
+ PARTITION BY LIST ( i_warehouse ) 
+ ( PARTITION inventory_table_1 VALUES (1), 
+ PARTITION inventory_table_2 VALUES (2) ); 
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
CREATE TABLE inventory_table( 
+ i_number SMALLINT PRIMARY KEY, 
+ i_name VARCHAR(20) NOT NULL, 
+ i_quantity INTEGER, 
+ i_warehouse SMALLINT ); 
+ CREATE TABLE inventory_table_1 
+ (CHECK ( i_warehouse = 1 )) 
+ INHERITS( inventory_table ); 
+ CREATE TABLE inventory_table_2 
+ (CHECK ( i_warehouse = 2 )) 
+ INHERITS( inventory_table ); 
+ ------------------------------------------------- 
+ CREATE FUNCTION TABLE_INSERT_TRIGGER() 
+ RETURNS TRIGGER AS $$ 
+ BEGIN 
+   IF ( NEW.i_warehouse = 1 ) THEN 
+     INSERT INTO inventory_table_1 
+ VALUES( NEW.*); 
+   ELSIF ( NEW.i_warehouse = 2 ) THEN 
+     INSERT INTO inventory_table_2 
+ VALUES( NEW.*); 
+ ELSE 
+   RAISE EXCEPTION 'Data out of range.  Fix the TABLE_INSERT_TRIGGER() function!'; 
+   END IF; 
+   RETURN NULL; 
+ END; 
+ $$ LANGUAGE plpgsql; 
+ ------------------------------------------------- 
+ CREATE TRIGGER TABLE_INSERT_TRIGGER 
+ BEFORE INSERT ON inventory_table 
+ FOR EACH ROW 
+ EXECUTE PROCEDURE TABLE_INSERT_TRIGGER();
+
+ + +#### 4.1.2 PARTITION Clause in a SELECT Statement + +Before a PARTITION clause in a SELECT statement can be migrated, the Oracle database partition must have been converted to child tables for PostgreSQL. + +**Description** + +A PARTITION clause treats only some partitions of the table (partition table) specified in the FROM clause as the targets of a query. + +#### 4.1.2.1 Queries Where a PARTITION Clause is Specified + +**Functional differences** + + - **Oracle database** + - A PARTITION clause can be specified. + - **PostgreSQL** + - A PARTITION clause cannot be specified. + +**Migration procedure** + +A PARTITION clause cannot be specified, so change the search target to a child table so that the same result is returned. Use the procedure below to perform migration. The migration sequence depends on whether a FOR clause is specified. + +1. Search for the keyword PARTITION and identify where it is specified in a SELECT statement. +2. Change the table name specified in the FROM clause to the name of the child table corresponding to the partition specified in the PARTITION clause. +3. Delete the PARTITION clause. + +**Migration example** + +The example below shows migration of a query that uses PARTITION. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SELECT * 
+ FROM inventory_table PARTITION( 
+ inventory_table_1);
+
+
SELECT * 
+ FROM inventory_table_1; 
+ 
+
+ +#### 4.1.2. Queries when FOR is Specified in a PARTITION Clause + +**Functional differences** + + - **Oracle database** + - FOR can be specified in a PARTITION clause. + - **PostgreSQL** + - A PARTITION clause cannot be specified. + +**Migration procedure** + +Use the following procedure to perform migration: + +1. Search for the keyword PARTITION and identify where it is specified in a SELECT statement. +2. Move the value specified in the PARTITION clause to a WHERE clause, and replace it with a conditional expression that compares the value with the column name written in the partition definition. +3. Delete the PARTITION clause. + +**Migration example** + +The example below shows migration when a PARTITION FOR clause is used to execute a query. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SELECT * 
+ FROM inventory_table PARTITION FOR (2);
+
+
SELECT * 
+ FROM inventory_table WHERE i_warehouse = 2;
+
+ +### 4.2 SELECT Statements + +This section explains SELECT statements. + +#### 4.2.1 UNIQUE + +**Description** + +UNIQUE deletes duplicate rows from the selected list and displays the result. + +**Functional differences** + + - **Oracle database** + - UNIQUE can be specified in a select list. + - **PostgreSQL** + - UNIQUE cannot be specified in a select list. Specify DISTINCT instead. + +**Migration procedure** + +Use the following procedure to perform migration: + +1. Search for the keyword UNIQUE and identify where it is specified in the select list of the SELECT statement. +2. Change UNIQUE in the SELECT statement to DISTINCT. + +**Migration example** + +The example below shows migration when UNIQUE is specified in a select list. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SELECT UNIQUE i_name 
+FROM inventory_table;
+
+
SELECT DISTINCT i_name 
+FROM inventory_table;
+
+ + +#### 4.2.2 Subqueries + +**Description** + +A subquery specifies a sub SELECT statement in the main SQL statement. + +**Functional differences** + + - **Oracle database** + - A subquery specified in a FROM clause can be executed even without an alias being specified for it. + - **PostgreSQL** + - A subquery specified in a FROM clause cannot be executed unless an alias is specified for it. + +**Migration procedure** + +Use the following procedure to perform migration: + +1. Search for the keyword SELECT and identify where a subquery is used. +2. If the subquery is specified in a FROM clause and no alias is specified for it, specify an alias. + +**Migration example** + +The example below shows migration when a SELECT statement that uses a subquery is executed. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SELECT * 
+ FROM ( SELECT * FROM inventory_table 
+ WHERE i_name = 'television' ) 
+ WHERE i_quantity > 0; 
+
+
SELECT * 
+ FROM ( SELECT * FROM inventory_table 
+ WHERE i_name = 'television' ) AS foo 
+ WHERE i_quantity > 0;
+
+ + +#### 4.2.3 Hierarchical Queries + +**Description** + +If a table contains data that can associate its own records, a hierarchical query displays the result of associating those records. + +##### 4.2.3.1 Executing Hierarchical Queries + +**Functional differences** + + - **Oracle database** + - Hierarchical queries can be used. + - **PostgreSQL** + - Hierarchical queries cannot be used. + +**Migration procedure** + +Hierarchical queries cannot be used, so specify a recursive query that uses a WITH clause so that the same result is returned. Use the following procedure to perform migration: + +1. Search for the keyword CONNECT and identify where a hierarchical query is used. +2. Check the following: + - Target table of the hierarchical query + - Column being used + - Conditional expressions specified in the CONNECT BY clause +3. Replace the hierarchical query with WITH clause syntax to match the format shown below. +4. Change the table name specified in the FROM clause to the name of the query in the WITH clause, and delete the CONNECT BY clause. + +~~~ +WITH RECURSIVE queryName( + columnUsed ) AS +( SELECT columnUsed + FROM targetTableOfHierarchicalQuery + UNION ALL + SELECT columnUsed(qualified by n) + FROM targetTableOfHierarchicalQuery n, + queryName w + WHERE conditionalExprOfConnectByClause(use w to qualify part qualified by PRIOR) ) +~~~ + +**Migration example** + +The example below shows migration when a hierarchical query is executed. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SELECT staff_id, name, manager_id 
+ FROM staff_table 
+ CONNECT BY PRIOR staff_id = manager_id; 
+
+
+
+
+
+
+
+
+
+
WITH RECURSIVE staff_table_w( staff_id, 
+ name, 
+ manager_id ) AS 
+ ( SELECT staff_id, name, manager_id 
+       FROM staff_table 
+     UNION ALL 
+     SELECT n.staff_id, n.name, n.manager_id 
+       FROM staff_table n, staff_table_w w 
+       WHERE w.staff_id = n.manager_id ) 
+ SELECT staff_id, name, manager_id 
+ FROM staff_table_w;
+
+ +##### 4.2.3.2 Hierarchical Query with Start Row + +**Functional differences** + + - **Oracle database** + - A START WITH clause can be specified in a hierarchical query to set start row conditions. + - **PostgreSQL** + - A START WITH clause cannot be specified. + +**Migration procedure** + +In a recursive query that uses a WITH clause, set a condition that is self-referencing so that the same result is returned. Use the following procedure to perform migration: + +1. Replace the hierarchical query with syntax that uses a recursive query (WITH clause). +2. If a START WITH clause is specified, move the specified conditional expression to the WHERE clause of the first subquery specified in the WITH clause. +3. Delete the START WITH clause. + +**Migration example** + +The example below shows migration when the start row is specified using a hierarchical query. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SELECT staff_id, name, manager_id 
+ FROM staff_table 
+ START WITH staff_id = '1001' 
+ CONNECT BY PRIOR staff_id = manager_id;
+
+
+
+
+
+
+
+
+
+
WITH RECURSIVE staff_table_w( staff_id, 
+ name, 
+ manager_id ) AS 
+ ( SELECT staff_id, name, manager_id 
+       FROM staff_table 
+       WHERE staff_id = '1001' 
+     UNION ALL 
+     SELECT n.staff_id, n.name, n.manager_id 
+       FROM staff_table n, staff_table_w w 
+       WHERE w.staff_id = n.manager_id ) 
+ SELECT staff_id, name, manager_id 
+ FROM staff_table_w;
+
+ +##### 4.2.3.3 Hierarchical Query Displaying the Hierarchical Position of Each Row + +**Functional differences** + + - **Oracle database** + - Specifying a LEVEL pseudocolumn in the select list of a hierarchical query displays the hierarchical position of each row. + - **PostgreSQL** + - A LEVEL pseudocolumn cannot be specified. + +**Migration procedure** + +In a recursive query that uses a WITH clause, create a result column equivalent to the LEVEL pseudocolumn as the query result of the WITH clause so that the same result is returned. Use the following procedure to perform migration: + +1. Replace the hierarchical query with syntax that uses a recursive query (WITH clause). +2. Add LEVEL to the column list of the query result of the WITH clause. +3. Specify the following values as the select list of the subquery in the position corresponding to the LEVEL column: + - Specify 1 in the first query. + - Specify LEVEL + 1 in the next query. (LEVEL is a column of the recursive table.) +4. Using a query, replace the portion where the LEVEL pseudocolumn is used with LEVEL (quoted identifier). + + +The following shows the conversion format containing the LEVEL pseudocolumn. + +~~~ +WITH RECURSIVE queryName( + columnUsed, "LEVEL" +) AS +( SELECT columnUsed, 1 + FROM targetTableOfHierarchicalQuery + UNION ALL + SELECT columnUsed(qualified by n), w."LEVEL" + 1 + FROM targetTableOfHierarchicalQuery n, + queryName w + WHERE conditionalExprOfConnectByClause(use w to qualify part qualified by PRIOR) ) +~~~ + + +**Migration example** + +The example below shows migration when a hierarchical query is used for displaying the hierarchical position of each row. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SELECT staff_id, name, manager_id, LEVEL 
+ FROM staff_table 
+ START WITH staff_id = '1001' 
+ CONNECT BY PRIOR staff_id = manager_id; 
+
+
+
+
+
+
+
+
+
+
+
+
+
+
WITH RECURSIVE staff_table_w( staff_id, 
+                name, 
+                manager_id, 
+                "LEVEL"  ) AS 
+   ( SELECT staff_id, name, manager_id, 1 
+       FROM staff_table 
+       WHERE staff_id = '1001' 
+     UNION ALL 
+     SELECT n.staff_id, 
+            n.name, 
+            n.manager_id, 
+            w."LEVEL" + 1 
+       FROM staff_table n, staff_table_w w 
+       WHERE w.staff_id = n.manager_id ) 
+ SELECT staff_id, name, manager_id, "LEVEL"  
+ FROM staff_table_w;
+
+ +##### 4.2.3.4 Hierarchical Query Displaying the Hierarchical Structure + +**Functional differences** + + - **Oracle database** + - Specifying SYS_CONNECT_BY_PATH in the select list of a hierarchical query displays the hierarchical structure. + - **PostgreSQL** + - SYS_CONNECT_BY_PATH cannot be specified. + +**Migration procedure** + +In a recursive query that uses a WITH clause, create an arithmetic expression that also uses the recursive query of the WITH clause so that the same result is returned. Use the following procedure to perform migration: + +1. Replace the hierarchical query with syntax that uses a recursive query (WITH clause). +2. Add PATH to the column list of the query result of the WITH clause. +3. Specify the following values for the select list of the subquery corresponding to the PATH column. The example here explains migration when a slash (/) is specified as the delimiter. +- In the first query, specify the path to the values of the columns from the root to the node. +- Specify m.PATH || '/' || n.columnName in the next query. (PATH is a recursive table column.) +4. Using a query, replace the part where PATH is used, with PATH. + +The following shows the conversion format containing PATH. + +~~~ +WITH RECURSIVE queryName( + columnUsed, PATH +) AS +( SELECT columnUsed, '/' || columnName + FROM targetTableOfHierarchicalQuery + UNION ALL + SELECT columnUsed(qualified by n), w.PATH || '/' || n.columnName + FROM targetTableOfHierarchicalQuery n, + queryName w + WHERE conditionalExprOfConnectByClause ) +~~~ + +For conditionalExprOfConnectByClause, use w to qualify the part qualified by PRIOR. + +**Migration example** + +The example below shows migration when the hierarchical structure is displayed. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SELECT staff_id,name,manager_id, 
+ SYS_CONNECT_BY_PATH(name,'/') AS PATH  
+ FROM staff_table 
+ START WITH staff_id = '1001' 
+ CONNECT BY PRIOR staff_id = manager_id; 
+
+
+
+
+
+
+
+
+
+
+
+
+
 WITH RECURSIVE staff_table_w( staff_id, 
+                name, 
+                manager_id, 
+                 PATH) AS 
+ ( SELECT staff_id,name,manager_id, '/' || name 
+ 	FROM staff_table 
+ 	WHERE staff_id = '1001' 
+ 	UNION ALL 
+ 	SELECT n.staff_id, 
+               name, 
+               n.manager_id, 
+               w.PATH || '/' || n.name 
+ 	FROM staff_table n,staff_table_w w 
+ 	WHERE w.staff_id = n.manager_id ) 
+ SELECT staff_id,name,manager_id, PATH 
+ FROM staff_table_w;
+
+ + +##### 4.2.3.5 Hierarchical Queries That Sort Each Hierarchy Level + +**Functional differences** + + - **Oracle database** + - Specifying an ORDER SIBLINGS BY clause in a hierarchical query enables sorting of each hierarchical level. + - **PostgreSQL** + - An ORDER SIBLINGS BY clause cannot be specified. + +**Migration procedure** + +Use the following procedure to perform migration: + +1. Replace the hierarchical query with syntax that uses a recursive query (WITH clause). +2. In the syntax that uses a recursive query (WITH clause), add poskey to the column list of the query result of the WITH clause. +3. Specify ROW_NUMBER() in the position corresponding to the poskey column. In the OVER clause, specify an ORDER BY clause that specifies the column of the ORDER SIBLINGS BY clause. +4. Add siblings_pos to the column list of the query result of the WITH clause. +5. Specify the following values as the select list of the subquery in the position corresponding to the siblings_pos column: + - Specify ARRAY[poskey] in the first query. + - Specify a concatenation of siblings_pos and poskey in the next query. +6. Using a query, specify siblings_pos in the ORDER BY clause to perform a sort. + +The following shows the conversion format containing sorting of each hierarchy level. + +~~~ +WITH RECURSIVE queryNameForPoskey( + columnUsed, poskey +) AS +( SELECT columnUsed, +ROW_NUMBER() OVER( ORDER BY columnNameSpecifiedInOrderSiblingsByClause ) + FROM targetTableOfHierarchicalQuery ), +WITH RECURSIVE queryName( + columnUsed, siblings_pos +) AS +( SELECT columnUsed, ARRAY[poskey] + FROM queryNameForPoskey + UNION ALL + SELECT columnUsed(qualified by n), w.siblings_pos || n.poskey + FROM queryNameForPoskey n, + queryName w + WHERE conditionalExprOfConnectByClause(use w to qualify part qualified by PRIOR ) ) +~~~ + +**Migration example** + +The example below shows migration when a hierarchical query is used to perform a sort in each hierarchy level. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SELECT staff_id, name, manager_id 
+  FROM staff_table 
+  START WITH staff_id = '1001' 
+  CONNECT BY PRIOR staff_id = manager_id 
+  ORDER SIBLINGS BY name; 
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
 WITH RECURSIVE staff_table_pos ( staff_id, 
+                name, 
+                manager_id, 
+                poskey ) AS 
+ ( SELECT staff_id, 
+          name, 
+          manager_id, 
+ ROW_NUMBER() OVER( ORDER BY name ) 
+ FROM staff_table ), 
+ staff_table_w ( staff_id, 
+                 name, 
+                 manager_id, 
+ siblings_pos ) AS 
+ ( SELECT staff_id, 
+          name, 
+          manager_id, 
+          ARRAY[poskey] 
+         FROM staff_table_pos 
+         WHERE staff_id = '1001' 
+       UNION ALL 
+       SELECT n.staff_id, 
+              n.name, 
+              n.manager_id, 
+              w.siblings_pos || n.poskey 
+         FROM staff_table_pos n, staff_table_w w 
+         WHERE w.staff_id = n.manager_id ) 
+ SELECT staff_id, name, manager_id 
+ FROM staff_table_w 
+ ORDER BY siblings_pos;
+
+ + +#### 4.2.4 MINUS + +**Description** + +MINUS finds the difference between two query results, that is, it returns rows that are in the first result set that are not in the second result set. + +**Functional differences** + + - **Oracle database** + - MINUS is specified to find the difference between two query results. + - **PostgreSQL** + - MINUS cannot be specified to find the difference between two query results. Specify EXCEPT instead. + +**Migration procedure** + +Use the following procedure to perform migration: + +1. Search for the keyword MINUS and identify where it is used. +2. Change MINUS to EXCEPT. + +**Migration example** + +The example below shows migration when the set difference of query results is to be found. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SELECT i_number, i_name FROM inventory_table 
+  WHERE i_warehouse = 2 
+ MINUS 
+ SELECT i_number, i_name FROM inventory_table 
+  WHERE i_name = 'cd';
+
+
SELECT i_number, i_name FROM inventory_table 
+  WHERE i_warehouse = 2 
+ EXCEPT 
+ SELECT i_number, i_name FROM inventory_table 
+  WHERE i_name = 'cd';
+
+ + +### 4.3 DELETE Statements +This section explains DELETE statements. + +#### 4.3.1 Omitting the FROM Keyword + +**Functional differences** + + - **Oracle database** + - The FROM keyword can be omitted from a DELETE statement. + - **PostgreSQL** + - The FROM keyword cannot be omitted from a DELETE statement. + +**Migration procedure** + +Use the following procedure to perform migration: + +1. Search for the keyword DELETE and identify where it is used. +2. If the FROM keyword has been omitted from the DELETE statement, add it. + +**Migration example** + +The example below shows migration when the FROM keyword is omitted from a DELETE statement. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
DELETE inventory_table 
+  WHERE i_name = 'cd player';
+
+
DELETE FROM inventory_table 
+  WHERE i_name = 'cd player';
+
+ + + +### 4.4 MERGE Statements +This section explains MERGE statements. + +#### 4.4.1 Executing MERGE Statements + +**Functional differences** + + - **Oracle database** + - MERGE statements can be used. + - **PostgreSQL** + - MERGE statements cannot be used. + +**Migration procedure** + +Use the following procedure to perform migration: + +1. Use an INSERT statement to specify the INSERT keyword that follows WHEN NOT MATCHED THEN in the MERGE statement. +2. Use a SELECT statement after the lines added in step 1 to specify the SELECT statement that follows the USING clause of the MERGE statement. +3. Use DO UPDATE in an ON CONFLICT clause of the INSERT statement specified in step 1 to specify the UPDATE keyword that follows WHEN MATCHED THEN in the MERGE statement. + +**Migration example** + +The example below shows how to migrate the MERGE statement. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
MERGE INTO new_inventory_table N 
+ USING ( SELECT i_number, 
+ i_name, 
+ i_quantity, 
+ i_warehouse 
+ FROM inventory_table ) I 
+ ON ( N.i_number = I.i_number ) 
+ WHEN MATCHED THEN 
+     UPDATE SET N.i_quantity = I.i_quantity 
+ WHEN NOT MATCHED THEN 
+     INSERT ( N.i_number, 
+ N.i_name, 
+ N.i_quantity, 
+ N.i_warehouse ) 
+     VALUES ( I.i_number, 
+ I.i_name, 
+ I.i_quantity, 
+ I.i_warehouse );
+
+
INSERT INTO new_inventory_table AS N ( 
+             i_number, 
+             i_name, 
+             i_quantity, 
+             i_warehouse 
+            ) 
+ SELECT i_number, 
+        i_name, 
+        i_quantity, 
+        i_warehouse 
+ FROM inventory_table 
+ ON CONFLICT (i_number) DO UPDATE 
+ SET i_quantity = excluded.i_quantity; 
+
+
+
+
+
+
+ +**Note** + +---- + +In the migration example shown above, a primary key or unique key definition must have been specified in the column specified in the ON clause of the MERGE statement. If using a table for which a primary key or unique key definition is not specified in the column of the conditional expression, refer to the migration example provided in "Information" below. + +---- + +**Information** + +---- + +The example below shows migration when a primary key or unique key definition is not specified in the column specified in the ON clause of the MERGE statement. + +**Migration procedure** + +Use the following procedure to perform migration: + +1. Use a SELECT statement in a WITH query to specify the SELECT statement that follows the USING clause of the MERGE statement. +2. Use an UPDATE statement in the WITH query to specify the UPDATE keyword that follows WHEN MATCHED THEN in the MERGE statement. +3. Specify the INSERT keyword that follows the WHEN NOT MATCHED THEN clause of the MERGE statement as an INSERT statement following the WITH query. +4. Specify NOT IN within the INSERT statement added in step 3 as an equivalent condition to the WHEN MATCHED THEN clause of the MERGE statement. + +**Migration example** + +The example below shows migration of a MERGE statement in which no primary key or unique key definition is specified. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
MERGE INTO new_inventory_table N 
+ USING ( SELECT i_number, 
+                i_name, 
+                i_quantity, 
+                i_warehouse 
+ FROM inventory_table ) I 
+ ON ( N.i_number = I.i_number ) 
+ WHEN MATCHED THEN 
+     UPDATE SET N.i_quantity = I.i_quantity 
+ WHEN NOT MATCHED THEN 
+     INSERT ( N.i_number, 
+              N.i_name, 
+              N.i_quantity, 
+              N.i_warehouse ) 
+     VALUES ( I.i_number, 
+              I.i_name, 
+              I.i_quantity, 
+              I.i_warehouse );
+
+
WITH I AS ( 
+ SELECT i_number, 
+        i_name, 
+        i_quantity, 
+        i_warehouse 
+ FROM inventory_table ), 
+ U AS ( 
+ UPDATE new_inventory_table AS N  
+ SET i_quantity = I.i_quantity  
+ FROM inventory_table I 
+ WHERE N.i_number = I.i_number 
+ RETURNING N.i_number ) 
+ INSERT INTO new_inventory_table ( 
+ SELECT * FROM I WHERE i_number NOT IN ( 
+ SELECT i_number FROM U ) ); 
+
+
+
+
+ +---- + +### 4.5 ALTER INDEX Statements + +**Description** + +An ALTER INDEX statement changes an index definition. + +#### 4.5.1 Restructuring an Index + +**Functional differences** + + - **Oracle database** + - A REBUILD clause can be specified in the ALTER INDEX statement to restructure an index. + - **PostgreSQL** + - A REBUILD clause cannot be specified in the ALTER INDEX statement. Use a REINDEX statement instead. + +**Migration procedure** + +Use the following procedure to perform migration: + +1. Search for the keywords ALTER and INDEX, and identify where they are used. +2. If a REBUILD clause is specified, replace the ALTER INDEX statement with a REINDEX statement. + +**Migration example** + +The example below shows migration when an index is restructured. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
ALTER INDEX idx REBUILD;
+
+
REINDEX INDEX idx;
+
+ +#### 4.5.2 Restructuring an Index Where a Tablespace is Specified + +**Functional differences** + + - **Oracle database** + - A tablespace can be specified in a REBUILD clause. + - **PostgreSQL** + - A REBUILD clause cannot be used. + +**Migration procedure** + +The REBUILD statement for an index restructure is replaced with a REINDEX statement, but a tablespace cannot be specified in this statement. Therefore, move the tablespace before performing the index restructure. Use the following procedure to perform migration: + +1. Search for the keywords ALTER and INDEX, and identify where they are used. +2. If both a REBUILD clause and a TABLESPACE clause are specified, replace the REBUILD clause of the ALTER INDEX statement with a SET clause. +3. Add a REINDEX statement after the ALTER INDEX statement. + +**Migration example** + +The example below shows migration when a tablespace is specified for restructuring an index. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
ALTER INDEX idx REBUILD TABLESPACE tablespace1;
+
+
+
ALTER INDEX idx SET TABLESPACE tablespace1; 
+ REINDEX INDEX idx;
+
+ + +#### 4.5.3 Restructuring an Index Where a Free Space Percentage is Specified + +**Functional differences** + + - **Oracle database** + - PCTFREE can be specified in a REBUILD clause to specify a free space percentage for an index. + - **PostgreSQL** + - A REBUILD clause cannot be used. + +**Migration procedure** + +The REBUILD statement for an index restructure is replaced with a REINDEX statement, but a free space percentage cannot be specified in this statement. Therefore, change the index utilization rate so that an equivalent result is returned. Then restructure the index. Use the following procedure to perform migration: + +1. Search for the keywords ALTER and INDEX, and identify where they are used. +2. If both a REBUILD clause and the PCTFREE keyword are specified, replace the REBUILD clause with a SET clause and change PCTFREE to FILLFACTOR. Use 100 - valueSpecifiedInPctfree as the set value. +3. Add a REINDEX statement after the ALTER INDEX statement. + +**Migration example** + +The example below shows migration when a fill factor is specified for restructuring an index. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
ALTER INDEX idx REBUILD PCTFREE 10;
+
+
ALTER INDEX idx SET (FILLFACTOR=90); 
+ REINDEX INDEX idx;
+
+ +### 4.6 ALTER SESSION Statements + +**Description** + +An ALTER SESSION statement specifies and changes parameters per session. + +#### 4.6.1 Closing dblink + +**Functional differences** + + - **Oracle database** + - An ALTER SESSION statement is used to close dblink. + - **PostgreSQL** + - ALTER SESSION statements cannot be used. Use DBLINK_CLOSE instead. + +**Migration procedure** + +Use the following procedure to perform migration: + +1. Search for the keywords ALTER and SESSION, and identify where they are used. +2. If a CLOSE DATABASE LINK clause is specified, delete the ALTER SESSION statement and replace it with a SELECT statement that calls DBLINK_CLOSE. + +**Migration example** + +The example below shows migration when dblink is closed. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
ALTER SESSION CLOSE DATABASE LINK dblink1;
+
+
SELECT DBLINK_CLOSE ( 'dblink1' );
+
+ +#### 4.6.2 Changing the Initialization Parameters + +**Functional differences** + + - **Oracle database** + - An ALTER SESSION statement is used to change the initialization parameters. + - **PostgreSQL** + - ALTER SESSION statements cannot be used. Use a SET statement instead to change the server parameters. + +**Migration procedure** + +Use the following procedure to perform migration: + +1. Search for the keywords ALTER and SESSION, and identify where they are used. +2. Replace the ALTER SESSION statement with a SET statement. The table below lists the corresponding initialization parameters and server parameters. + +**Corresponding initialization parameters and server parameters** + +|Initialization parameter|Runtime configuration parameters of PostgreSQL| +|:---|:---| +| ENABLE_DDL_LOGGING | log_statement | +| NLS_CURRENCY | lc_monetary | +| NLS_DATE_FORMAT | DateStyle | +| NLS_DATE_LANGUAGE | lc_time | +| NLS_TIMESTAMP_FORMAT | lc_time | +| NLS_TIMESTAMP_TZ_FORMAT | lc_time | +| OPTIMIZER_INDEX_COST_ADJ | cpu_index_tuple_cost
seq_page_cost | + + + +**Migration example** + +The example below shows migration when the initialization parameters are changed. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
ALTER SESSION SET ENABLE_DDL_LOGGING = TRUE;
+
+
SET log_statement = 'DDL';
+
+ +**Note** + +---- + +The values that can be specified for server parameters may differ from those that can be specified for the initialization parameters. Refer to the manual provided by the vendor for the values that can be specified. + +---- + +**See** + +---- + +Refer to "Server Configuration" in "Server Administration" in the PostgreSQL Documentation for information on server parameters. + +---- + + +#### 4.6.3 Setting Transaction Characteristics + +**Functional differences** + + - **Oracle database** + - An ALTER SESSION statement is used to set transaction characteristics. + - **PostgreSQL** + - ALTER SESSION statements cannot be used. Use a SET TRANSACTION statement instead. + +**Migration procedure** + +Use the following procedure to perform migration: + +1. Search for the keywords ALTER and SESSION, and identify where they are used. +2. If SET ISOLATION_LEVEL is specified, replace the ALTER SESSION statement with a SET TRANSACTION statement. + +**Migration example** + +The example below shows migration when transaction characteristics are set. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
ALTER SESSION SET
+ ISOLATION_LEVEL = SERIALIZABLE;
+
+
SET TRANSACTION 
+ ISOLATION LEVEL SERIALIZABLE;
+
+ +#### 4.6.4 Migrating the Time Zone Setting + +**Functional differences** + + - **Oracle database** + - An ALTER SESSION statement is used to set the time zone. + - **PostgreSQL** + - ALTER SESSION statements cannot be used. Use a SET statement instead. + +**Migration procedure** + +Use the following procedure to perform migration: + +1. Search for the keywords ALTER and SESSION, and identify where they are used. +2. If SET TIME_ZONE is specified, replace the ALTER SESSION statement with a SET statement. + +**Migration example** + +The example below shows migration when the time zone is set. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
ALTER SESSION SET TIME_ZONE = '+09:00';
+
+
SET TimeZone='Japan';
+
+ + +**Note** + +---- + +Be sure to use the full time zone name when specifying the time zone in the TimeZone parameter of PostgreSQL. + +---- + +### 4.7 GRANT Statements + +**Description** + +A GRANT statement defines access privileges. + +#### 4.7.1 Migratability of GRANT Statement Features + +The following table indicates the migratability of the GRANT statement features provided by Oracle databases to PostgreSQL. + +**System privileges** + +|GRANT statement features of Oracle databases|Migratability|Remarks| +|:---|:---:|:---| +| Granting of system privileges | MR | PUBLIC cannot be specified for a grantee.
There are restrictions on the privileges that can be migrated. | +| Granting of role (role) | YR | PUBLIC cannot be specified for a grantee. | +| Granting of all system privileges (ALL PRIVILEGES) | Y | | +| WITH ADMIN OPTION clause | MR | Only privileges that can be migrated with the GRANT statement. | +| WITH DELEGATE OPTION clause | N | | + +**Object privileges** + +|GRANT statement features of Oracle databases|Migratability|Remarks| +|:---|:---:|:---| +| Granting of object privileges | YR | Columns can be specified.
There are restrictions on the privileges that can be migrated. | +| Granting of all object privileges (ALL [PRIVILEGES]) | Y | Columns can be specified. | +| Grantee Schema object | Y | | +| Grantee User | N | | +| Grantee Directory | N | | +| Grantee Edition | N | | +| Grantee Mining model | N | | +| Grantee Java source | N | | +| Grantee SQL translation profile | N | | +| WITH HIERARCHY OPTION clause | N | | +| WITH GRANT OPTION clause | Y | | + +**Program unit privileges** + +|GRANT statement features of Oracle databases|Migratability|Remarks| +|:---|:---|:---| +| Granting of program unit privileges | N | | + +Y: Syntax can be migrated as is +YR: Syntax can be migrated as is with restrictions +MR: Modified syntax can be migrated with restrictions +N: Cannot be migrated + + +#### 4.7.2 Granting System Privileges +##### 4.7.2.1 Granting Privileges for Operating Users and Roles + +**Functional differences** + + - **Oracle database** + - A GRANT statement is used to grant privileges for creating and changing users and roles. + - **PostgreSQL** + - A GRANT statement cannot be used to grant privileges for creating and changing users and roles. Use an ALTER ROLE statement instead. + +**Migration procedure** + +Use the following procedure to perform migration: + +1. Search for the keyword GRANT and identify where it is used. +2. If privileges for creating and changing users and roles are granted, replace the GRANT statement with the ALTER ROLE statement. The table below lists the migratable user and role operation privileges. + +**Migratable user and role operation privileges** + + - **ROLES** + +|GRANT statement in Oracle database|Corresponding ALTER ROLE statement in PostgreSQL| +|:---|:---:| +|GRANT CREATE ROLE TO *roleName*
GRANT ALTER ANY ROLE TO *roleName*
GRANT DROP ANY ROLE TO *roleName*
GRANT ANY ROLE TO *roleName* | ALTER ROLE *roleName* CREATEROLE;| + + - **USERS** + +|GRANT statement in Oracle database|Corresponding ALTER ROLE statement in PostgreSQL| +|:---|:---:| +|GRANT CREATE USER TO *userName*
GRANT ALTER USER TO *userName*
GRANT DROP USER TO *userName*|ALTER ROLE *userName* CREATEUSER| + + +**Migration example** + +The example below shows migration when role creation privileges are granted. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
GRANT CREATE ROLE TO role1;
+
+
ALTER ROLE role1 CREATEROLE;
+
+ +#### 4.7.2.2 Granting Privileges for Creating Objects + +**Functional differences** + + - **Oracle database** + - A GRANT statement is used to grant creation privileges per object. + - **PostgreSQL** + - A GRANT statement is used to grant object creation privileges per schema or database. + +**Migration procedure** + +Use the following procedure to perform migration: + +1. Search for the keyword GRANT and identify where it is used. +2. If creation privileges are granted per object, replace the GRANT statement with a GRANT statement that grants creation privileges per schema or database. The table below lists the migratable object creation privileges. + +**Migratable object creation privileges** + +|Object|GRANT statement in Oracle database|Corresponding ALTER ROLE statement in PostgreSQL| +|:---|:---|:---| +| MATERIALIZED VIEWS | GRANT CREATE MATERIALIZED VIEW TO *userName* | GRANT CREATE ON SCHEMA *schemaName* TO *userName* | +| OPERATORS | GRANT CREATE OPERATOR TO *userName* | GRANT CREATE ON SCHEMA *schemaName* TO *userName* | +| PROCEDURES | GRANT CREATE PROCEDURE TO *userName* | GRANT CREATE ON SCHEMA *schemaName* TO *userName* | +| SEQUENCES | GRANT CREATE SEQUENCE TO *userName* | GRANT CREATE ON SCHEMA *schemaName* TO *userName* | +| SESSIONS | GRANT CREATE SESSION TO *userName* | GRANT CONNECT ON DATABASE *databaseName* TO *userName* | +| TABLES | GRANT CREATE TABLE TO *userName* | GRANT CREATE ON SCHEMA *schemaName* TO *userName* | +| TRIGGERS | GRANT CREATE TRIGGER TO *userName* | GRANT CREATE ON SCHEMA *schemaName* TO *userName* | +| TYPES | GRANT CREATE TYPE TO *userName* | GRANT CREATE ON SCHEMA *schemaName* TO *userName* | +| VIEWS | GRANT CREATE VIEW TO *userName* | GRANT CREATE ON SCHEMA *schemaName* TO *userName* | + +**Migration example** + +The example below shows migration when table creation privileges are granted. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
GRANT CREATE TABLE TO user1;
+
+
GRANT CREATE ON SCHEMA scm1 TO user1;
+
+ +##### 4.7.2.3 Granting Roles (with Password Setting) + +**Functional differences** + + - **Oracle database** + - When a GRANT statement is used to assign a user to a role, a password can be set at the same time. + - **PostgreSQL** + - When a GRANT statement is used to assign a user to a role, a password cannot be set at the same time. + +**Migration procedure** + +To set a password, you must specify a separate CREATE USER or ALTER USER statement and set the password in that statement. Use the following procedure to perform migration: + +1. Search for the keyword GRANT and identify where it is used. +2. If an IDENTIFIED BY clause is specified, check if the target user exists. +3. If the user exists, use an ALTER USER statement to change the password. If the user does not exist, use a CREATE USER statement to create a new user and set a password. +4. Delete the clause for granting a password from the GRANT statement. + +**Migration example** + +The example below shows migration when role1 is granted to user1. + + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
GRANT role1 TO user1 IDENTIFIED BY PASSWORD;
+
+
 CREATE USER user1 PASSWORD 'PASSWORD'; 
+ GRANT role1 TO user1;
+
+ +#### 4.7.3 Granting Object Privileges + +There is no difference in the syntax of GRANT statements with regard to object privileges that are migratable from an Oracle database. However, some object privileges cannot be used in PostgreSQL, so they must be changed to supported object privileges. + +The table below lists the object privileges that can be migrated from an Oracle database to PostgreSQL. + +**Migratable object privileges** + + - **Materialized view privileges** + +|Name of object privilege|Change required|Remarks| +|:---|:---:|:---| +| READ | Yes | Change to SELECT. | +| SELECT | No | If a FOR UPDATE clause is used in the SELECT statement, UPDATE privileges are also required. | + + - **Operator privileges** + +|Name of object privilege|Change required|Remarks| +|:---|:---:|:---| +EXECUTE | Yes | In PostgreSQL, EXECUTE privileges must be granted to a function that implements operators.| + + - **Procedure, function, and package privileges** + +|Name of object privilege|Change required|Remarks| +|:---|:---:|:---| +| EXECUTE | Yes | The FUNCTION keyword must be added before the function name. | + + - **Sequence privileges** + +|Name of object privilege|Change required|Remarks| +|:---|:---:|:---| +| SELECT | Yes | Change to USAGE.
The SEQUENCE keyword must be added before the sequence name. | + + - **Table privileges** + +|Name of object privilege|Change required|Remarks| +|:---|:---:|:---| +| DELETE | No | | +| INSERT | No | | +| READ | Yes | Change to SELECT. | +| REFERENCES | No | | +| SELECT | No | If a FOR UPDATE clause is used in the SELECT statement, UPDATE privileges are also required. | +| UPDATE | No | | + + - **View privileges** + +|Name of object privilege|Change required|Remarks| +|:---|:---:|:---| +| DELETE | No | | +| INSERT | No | | +| READ | Yes | Change to SELECT. | +| REFERENCES | No | | +| SELECT | No | If a FOR UPDATE clause is used in the SELECT statement, UPDATE privileges are also required. | +| UPDATE | No | | + + diff --git a/contrib/orafce/doc/sql_migration/sql_migration05.md b/contrib/orafce/doc/sql_migration/sql_migration05.md new file mode 100644 index 000000000..cd96cb3e6 --- /dev/null +++ b/contrib/orafce/doc/sql_migration/sql_migration05.md @@ -0,0 +1,1872 @@ +Chapter 5 Migrating PL/SQL +--- + +This chapter explains how to migrate Oracle database PL/SQL. Note that in this document, PL/SQL refers to the language to be migrated to PostgreSQL PL/pgSQL. + +### 5.1 Notes on Migrating from PL/SQL to PL/pgSQL +This section provides notes on migration from PL/SQL to PL/pgSQL. + +#### 5.1.1 Transaction Control + +PL/pgSQL does not allow transaction control within a process. Terminate a procedure whenever a transaction is terminated in the Oracle database and execute a transaction control statement from the application. + +### 5.2 Basic Elements +This section explains how to migrate the basic elements of PL/SQL. + +#### 5.2.1 Migrating Data Types +The table below lists the PostgreSQL data types that correspond to data types unique to PL/SQL. + +Data type correspondence with PL/SQL + + - **Character** + +|Oracle database Data type|Remarks|Migratability|PostgreSQL Data type|Remarks| +|:---|:---|:---:|:---|:---| +| STRING | The number of bytes or number of characters can be specified. | MR | varchar | Only the number of characters can be specified. | + + - **Numeric** + +|Oracle database Data type|Remarks|Migratability|PostgreSQL Data type|Remarks| +|:---|:---|:---:|:---|:---| +| BINARY_INTEGER | | M | integer | | +| NATURAL | | M | integer | | +| NATURALN | Type with NOT NULL constraints | MR | integer | Set "not null" constraints for variable declarations. | +| PLS_INTEGER | | M | integer | | +| POSITIVE | | M | integer | | +| POSITIVEN | Type with NOT NULL constraints | MR | integer | Set "not null" constraints for variable declarations. | +| SIGNTYPE | | M | smallint | | +| SIMPLE_DOUBLE | Type with NOT NULL constraints | MR | double precision | Set "not null" constraints for variable declarations. | +| SIMPLE_FLOAT | Type with NOT NULL constraints | MR | real | Set "not null" constraints for variable declarations. | +| SIMPLE_INTEGER | Type with NOT NULL constraints | MR | integer | Set "not null" constraints for variable declarations. | + + - **Date and time** + +|Oracle database Data type|Remarks|Migratability|PostgreSQL Data type|Remarks| +|:---|:---|:---:|:---|:---| +| DSINTERVAL_UNCONSTRAINED | | N | | | +| TIME_TZ_UNCONSTRAINED | | N | | | +| TIME_UNCONSTRAINED | | N | | | +| TIMESTAMP_LTZ_UNCONSTRAINED | | N | | | +| TIMESTAMP_TZ_UNCONSTRAINED | | N | | | +| TIMESTAMP_UNCONSTRAINED | | N | | | +| YMINTERVAL_UNCONSTRAINED | | N | | | + + - **Other** + +|Oracle database Data type|Remarks|Migratability|PostgreSQL Data type|Remarks| +|:---|:---|:---:|:---|:---| +| BOOLEAN | | Y | boolean | | +| RECORD | | M | Complex type | | +| REF CURSOR (cursor variable) | | M | refcursor type | | +| Subtype with constraints | | N | | | +| Subtype that uses the base type within the same data type family | | N | | | +| Unconstrained subtype | | N | | | + +Y: Data type can be migrated as is + +M: Modified data type can be migrated + +N: Cannot be migrated + +MR: Modified data type can be migrated with restrictions + + +**See** + +---- + +Refer to "Data Types" for information on migrating data types other than those unique to PL/SQL. + +---- + + +#### 5.2.2 Error-Related Elements +This section explains elements related to PL/SQL errors. + +##### 5.2.2.1 Predefined Exceptions + +**Description** + +A predefined exception is an error defined beforehand in an Oracle database. + +**Functional differences** + + - **Oracle database** + - Predefined exceptions can be used. + - **PostgreSQL** + - Predefined exceptions cannot be used. Use PostgreSQL error codes instead. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Identify where predefined exceptions are used. + 2. Refer to the table below and replace the values of predefined exceptions with PostgreSQL error codes. + +|Predefined exception
(Oracle database)|Migratability|Corresponding PostgreSQL error code| +|:---|:---:|:---| +| ACCESS_INTO_NULL | N | Not generated | +| CASE_NOT_FOUND | Y | case_not_found | +| COLLECTION_IS_NULL | N | Not generated | +| CURSOR_ALREADY_OPEN | Y | duplicate_cursor | +| DUP_VAL_ON_INDEX | Y | unique_violation | +| INVALID_CURSOR | Y | invalid_cursor_name | +| INVALID_NUMBER | Y | invalid_text_representation | +| LOGIN_DENIED | Y | invalid_authorization_specification
invalid_password | +| NO_DATA_FOUND | Y | no_data_found | +| NO_DATA_NEEDED | N | Not generated | +| NOT_LOGGED_ON | N | Not generated | +| PROGRAM_ERROR | Y | internal_error | +| ROWTYPE_MISMATCH | N | Not generated | +| SELF_IS_NULL | N | Not generated | +| STORAGE_ERROR | Y | out_of_memory | +| SUBSCRIPT_BEYOND_COUNT | N | Not generated | +| SUBSCRIPT_OUTSIDE_LIMIT | N | Not generated | +| SYS_INVALID_ROWID | N | Not generated | +| TIMEOUT_ON_RESOURCE | N | Not generated | +| TOO_MANY_ROWS | Y | too_many_rows | +| VALUE_ERROR | Y | null_value_not_allowed
invalid_text_representation
string_data_right_truncation
invalid_parameter_value | +| ZERO_DIVIDE | Y | division_by_zero | + + +Y: Can be migrated + +N: Cannot be migrated + + +**Migration example** + +The example below shows how to migrate the VALUE_ERROR exception. Note that OR is used in the migration example to group error codes so that VALUE_ERROR corresponds to multiple PostgreSQL error codes. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SET SERVEROUTPUT ON; 
+ DECLARE 
+  variety VARCHAR2(20) := 'television'; 
+  company VARCHAR2(20) := 'Fullmoon Industry'; 
+  name VARCHAR2(30); 
+ BEGIN 
+
+ name := ( variety || 'from' || company ); + EXCEPTION + WHEN VALUE_ERROR THEN +
+
+
+ DBMS_OUTPUT.PUT_LINE ( + 'ERR: Category length is out of range.' ); + END; + / +
+
+
DO $$ 
+ DECLARE 
+  variety VARCHAR(20) := 'television'; 
+  company VARCHAR(20) := 'Fullmoon Industry'; 
+ name VARCHAR(30); 
+ BEGIN 
+  PERFORM DBMS_OUTPUT.SERVEROUTPUT(TRUE); 
+  name := ( variety || 'from' || company ); 
+ EXCEPTION 
+  WHEN null_value_not_allowed 
+       OR invalid_text_representation 
+       OR string_data_right_truncation 
+       OR invalid_parameter_value THEN 
+  PERFORM DBMS_OUTPUT.PUT_LINE ( 
+   'ERR: Category length is out of range.' ); 
+ END; 
+ $$ 
+ ; 
+ 
+
+ +##### 5.2.2.2 SQLCODE + +**Description** + +SQLCODE returns the error code of an error. + +**Functional differences** + + - **Oracle database** + - SQLCODE can be specified to obtain an error code. + - **PostgreSQL** + - SQLCODE cannot be specified to obtain an error code. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword SQLCODE and identify where it is used. + 2. Change the portion that calls SQLCODE to SQLSTATE. + +**Migration example** + +The example below shows migration when the code of an error is displayed. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SET SERVEROUTPUT ON; 
+ DECLARE 
+  v_i_number SMALLINT := 401; 
+  v_i_name VARCHAR2(30) := 'Blu-ray and DVD recorder'; 
+  v_i_quantity INTEGER := 10; 
+  v_i_warehouse SMALLINT := 2; 
+ BEGIN 
+
+ INSERT INTO inventory_table + VALUES ( v_i_number, + v_i_name, + v_i_quantity, + v_i_warehouse ); + EXCEPTION + WHEN OTHERS THEN + DBMS_OUTPUT.PUT_LINE( + 'ERR:' || SQLCODE || + ': Failure of INSERT.' ); + END; + / +
+
+
DO $$ 
+ DECLARE 
+  v_i_number SMALLINT := 401; 
+  v_i_name VARCHAR(30) := 'Blu-ray and DVD recorder'; 
+  v_i_quantity INTEGER := 10; 
+  v_i_warehouse SMALLINT := 2; 
+ BEGIN 
+ PERFORM DBMS_OUTPUT.SERVEROUTPUT(TRUE); 
+ INSERT INTO inventory_table 
+  VALUES ( v_i_number, 
+           v_i_name, 
+           v_i_quantity, 
+           v_i_warehouse ); 
+ EXCEPTION 
+  WHEN OTHERS THEN 
+   PERFORM DBMS_OUTPUT.PUT_LINE( 
+    'ERR:' || SQLSTATE || 
+    ': Failure of INSERT.' ); 
+ END; 
+ $$ 
+ ;
+
+ +**Note** + +---- + +Oracle databases and PostgreSQL have different error codes, so the set SQLCODE values and SQLSTATE values are different. Refer to "Appendix A. PostgreSQL Error Codes" in the PostgreSQL Documentation for information on the error codes to be defined in PostgreSQL. + +---- + +##### 5.2.2.3 EXCEPTION Declarations + +**Description** + +An EXCEPTION declaration defines an error. + +**Functional differences** + + - **Oracle database** + - EXCEPTION declarations can be used to define errors. + - **PostgreSQL** + - EXCEPTION declarations cannot be used. + +**Migration procedure** + +EXCEPTION declarations cannot be used, so specify the error number in a RAISE statement to achieve equivalent operation. Use the following procedure to perform migration: + + 1. Search for the keyword EXCEPTION, identify where an EXCEPTION declaration is used, and check the error name. + 2. Search for the keyword RAISE and identify where the error created using the EXCEPTION declaration is used. + 3. Delete the error name from the RAISE statement and instead specify the error code using ERRCODE in a USING clause. + 4. Change the portion of the EXCEPTION clause where the error name is used to capture the error to SQLSTATE 'errCodeSpecifiedInStep3'. + 5. Delete the EXCEPTION declaration. + +**Migration example** + +The example below shows migration when a user-defined error is generated. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SET SERVEROUTPUT ON; 
+ DECLARE 
+  v_i_number SMALLINT := 200; 
+  v_i_name VARCHAR2(20) := 'television'; 
+  v_i_quantity INTEGER := 10; 
+  v_i_warehouse SMALLINT := 3; 
+  warehouse_num_err EXCEPTION; 
+ BEGIN 
+
+ IF ( v_i_warehouse = 1 ) OR ( v_i_warehouse = 2 ) THEN + INSERT INTO inventory_table + VALUES ( v_i_number, + v_i_name, + v_i_quantity, + v_i_warehouse ); + ELSE + RAISE warehouse_num_err; + END IF; + EXCEPTION + WHEN warehouse_num_err THEN + DBMS_OUTPUT.PUT_LINE( + 'ERR: Warehouse number is out of range.' ); +
+ END; + /
+
+
SET SERVEROUTPUT ON; 
+ DECLARE 
+  v_i_number SMALLINT := 200; 
+  v_i_name VARCHAR2(20) := 'television'; 
+  v_i_quantity INTEGER := 10; 
+  v_i_warehouse SMALLINT := 3; 
+
+ BEGIN +
+ IF ( v_i_warehouse = 1 ) OR ( v_i_warehouse = 2 ) THEN + INSERT INTO inventory_table + VALUES ( v_i_number, + v_i_name, + v_i_quantity, + v_i_warehouse ); + ELSE + RAISE USING ERRCODE = '20001'; + END IF; + EXCEPTION + WHEN SQLSTATE '20001' THEN + DBMS_OUTPUT.PUT_LINE( + 'ERR: Warehouse number is out of range.' ); +
+ END; + /
+
+ +##### 5.2.2.4 PRAGMA EXCEPTION_INIT and RAISE_APPLICATION_ERROR + +**Description** + +An EXCEPTION_INIT pragma associates a user-defined error name with an Oracle database error code. RAISE_APPLICATION_ERROR uses a user-defined error code and error message to issue an error. + +**Functional differences** + + - **Oracle database** + - EXCEPTION_INIT pragmas and RAISE_APPLICATION_ERROR statements can be used. + - **PostgreSQL** + - EXCEPTION_INIT pragmas and RAISE_APPLICATION_ERROR statements cannot be used. + +**Migration procedure** + +EXCEPTION_INIT pragmas and RAISE_APPLICATION_ERROR statements cannot be used, so specify an error message and error code in a RAISE statement to achieve equivalent operation. Use the following procedure to perform migration: + + 1. Search for the keywords EXCEPTION and PRAGMA, and check for an EXCEPTION_INIT pragma and the specified error and error code. + 2. Search for the keyword RAISE_APPLICATION_ERROR and check where an error is used. + 3. Replace the error message and error code called by RAISE_APPLICATION_ERROR with syntax that uses a USING clause in RAISE. + 4. Change the portion of the EXCEPTION clause where the user-defined error name is used to capture the error to SQLSTATE 'errCodeSpecifiedInStep3'. To display the error message and error code in the EXCEPTION clause, use SQLERRM and SQLSTATE. + 5. Delete the EXCEPTION declaration and EXCEPTION INIT pragma. + +**Migration example** + +The example below shows migration when an EXCEPTION INIT pragma and RAISE APPLICATION ERROR statement are used. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SET SERVEROUTPUT ON; 
+ DECLARE 
+  v_i_number SMALLINT := 200; 
+  v_i_name VARCHAR2(30) := ' liquid crystal?television'; 
+  v_i_quantity INTEGER := 10; 
+  v_i_warehouse SMALLINT := 3; 
+  invalid_length EXCEPTION; 
+  PRAGMA EXCEPTION_INIT ( invalid_length, -20001 ); 
+ BEGIN 
+
+ IF ( LENGTH( v_i_name ) <= 20 ) THEN + INSERT INTO inventory_table + VALUES ( v_i_number, + v_i_name, + v_i_quantity, + v_i_warehouse ); + ELSE + RAISE_APPLICATION_ERROR( + -20001, 'ERR: i_name is invalid length.' ); + END IF; + EXCEPTION + WHEN invalid_length THEN + DBMS_OUTPUT.PUT_LINE( + TO_CHAR(SQLERRM(-20001)) ); +
+ END; + /
+
+
DO $$ 
+ DECLARE 
+  v_i_number SMALLINT := 200; 
+  v_i_name VARCHAR(30) := ' liquid crystal television'; 
+  v_i_quantity INTEGER := 10; 
+  v_i_warehouse SMALLINT := 3; 
+
+
+ BEGIN + PERFORM DBMS_OUTPUT.SERVEROUTPUT( TRUE ); + IF ( LENGTH( v_i_name ) <= 20 ) THEN + INSERT INTO inventory_table + VALUES ( v_i_number, + v_i_name, + v_i_quantity, + v_i_warehouse ); + ELSE + RAISE 'ERR: i_name is invalid length.' + USING ERRCODE = '20001'; + END IF; + EXCEPTION + WHEN SQLSTATE '20001' THEN + PERFORM DBMS_OUTPUT.PUT_LINE( + SQLSTATE || ':' || SQLERRM ); + END; + $$ + ;
+
+ +**Note** + +---- + +SQLERRM provided by PostgreSQL cannot specify an error code in its argument. + +---- + +##### 5.2.2.5 WHENEVER + +**Description** + +WHENEVER SQLERROR predefines the processing to be run when an error occurs in an SQL statement or PL/SQL. +WHENEVER OSERROR predefines the processing to be run when an operating system error occurs. + +**Functional differences** + + - **Oracle database** + - WHENEVER can be used to predefine the processing to be run when an error occurs. + - **PostgreSQL** + - WHENEVER cannot be used. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword WHENEVER and identify where it is used. + 2. Replace WHENEVER SQLERROR EXIT FAILURE syntax or WHENEVER OSERROR EXIT FAILURE syntax with \set ON_ERROR_STOP ON. + +**Migration example** + +The example below shows migration when an active script that encounters an error is stopped. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
WHENEVER SQLERROR EXIT FAILURE 
+
+ DECLARE + v_i_number SMALLINT := 401; + v_i_name VARCHAR2(30) := 'liquid crystal television'; + v_i_quantity INTEGER := 100; + v_i_warehouse SMALLINT := 2; + BEGIN + INSERT INTO inventory_table + VALUES ( v_i_number, + v_i_name, + v_i_quantity, + v_i_warehouse ); + END; + / +
+
+
\set ON_ERROR_STOP ON 
+ DO $$ 
+ DECLARE 
+  v_i_number SMALLINT := 401; 
+  v_i_name VARCHAR(30) := 'liquid crystal television'; 
+  v_i_quantity INTEGER := 100; 
+  v_i_warehouse SMALLINT := 2; 
+ BEGIN 
+  INSERT INTO inventory_table 
+   VALUES ( v_i_number, 
+            v_i_name, 
+            v_i_quantity, 
+            v_i_warehouse ); 
+ END; 
+ $$ 
+ ;
+
+ + +**Note** + +---- + + - WHENEVER SQLERROR and WHENEVER OSERROR are SQL*Plus features. Migrate them to the psql feature in PostgreSQL. + - Of the values that can be specified in WHENEVER, only EXIT FAILURE and CONTINUE NONE can be migrated. If CONTINUE NONE is specified, replace it with \set ON_ERROR_ROLLBACK ON. + +---- + +#### 5.2.3 Cursor-Related Elements + +This section explains elements related to PL/SQL cursors. + +##### 5.2.3.1 %FOUND + +**Description** + +%FOUND obtains information on whether an SQL statement affected one or more rows. + +**Functional differences** + + - **Oracle database** + - %FOUND can be used. + - **PostgreSQL** + - %FOUND cannot be used. Use FOUND instead. + +**Migration procedure** + +Use the following procedure to perform migration with FOUND: + + - When there is one implicit or explicit cursor + 1. Search for the keyword %FOUND and identify where it is used. + 2. Change the portion that calls cursorName%FOUND to FOUND. + - When there are multiple explicit cursors + 1. Search for the keyword %FOUND and identify where it is used. + 2. Using DECLARE, declare the same number of BOOLEAN variables as explicit cursors. + 3. Immediately after each FETCH statement, store the value obtained by FOUND in the variable declared in step 2. + 4. Replace the portion that calls cursorName%FOUND with the variable used in step 3. + +**Migration example** + +The example below shows migration when update of a row by an implicit cursor is checked. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SET SERVEROUTPUT ON; 
+ BEGIN 
+
+ UPDATE inventory_table SET i_warehouse = 3 + WHERE i_name = 'television'; + IF SQL%FOUND THEN + DBMS_OUTPUT.PUT_LINE ( 'Updated!' ); + ELSE + DBMS_OUTPUT.PUT_LINE ( 'Not Updated!' ); + END IF; + END; + / +
+
+
DO $$ 
+ BEGIN 
+  PERFORM DBMS_OUTPUT.SERVEROUTPUT(TRUE); 
+  UPDATE inventory_table SET i_warehouse = 3 
+   WHERE i_name = 'television'; 
+  IF FOUND THEN 
+   PERFORM DBMS_OUTPUT.PUT_LINE( 'Updated!' ); 
+  ELSE 
+   PERFORM DBMS_OUTPUT.PUT_LINE( 'Not updated!' ); 
+  END IF; 
+ END; 
+ $$ 
+ ;
+
+ +**Note** + +---- + +Statements in which %FOUND is determined to be NULL cannot be migrated. If SQL has not been executed at all, FOUND is set to FALSE, which is the same return value as when no row has been affected. + +---- + +##### 5.2.3.2 %NOTFOUND + +**Description** + +%NOTFOUND obtains information on whether an SQL statement affected no rows. + +**Functional differences** + + - **Oracle database** + - %NOTFOUND can be used. + - **PostgreSQL** + - %NOTFOUND cannot be used. Use NOT FOUND instead. + +**Migration procedure** + +Use the following procedure to perform migration: + + - When there is one implicit or explicit cursor + 1. Search for the keyword %NOTFOUND and identify where it is used. + 2. Change the portion that calls cursorName%NOTFOUND to NOT FOUND. + - When there are multiple explicit cursors + 1. Search for the keyword %NOTFOUND and identify where it is used. + 2. Using DECLARE, declare the same number of BOOLEAN variables as explicit cursors. + 3. Immediately after each FETCH statement, store the value obtained by FOUND in the variable declared in step 2. + 4. Replace the portion that calls cursorName%NOTFOUND with negation of the variable used in step 3. + +**Migration example** + +The example below shows migration when multiple explicit cursors are used to repeat FETCH until there are no more rows in one of the tables. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 SET SERVEROUTPUT ON; 
+ DECLARE 
+  CURSOR cur1 IS 
+   SELECT i_number, i_name 
+    FROM inventory_table 
+    WHERE i_name = 'television'; 
+  CURSOR cur2 IS 
+   SELECT i_number, i_name 
+    FROM inventory_table 
+    WHERE i_name = 'cd player'; 
+  v1_i_number inventory_table.i_number%TYPE; 
+  v2_i_number inventory_table.i_number%TYPE; 
+  v1_i_name inventory_table.i_name%TYPE; 
+  v2_i_name inventory_table.i_name%TYPE; 
+
+
+ BEGIN +
+ OPEN cur1; + OPEN cur2; + LOOP + FETCH cur1 into v1_i_number, v1_i_name; +
+ FETCH cur2 into v2_i_number, v2_i_name; +
+ EXIT WHEN ( cur1%NOTFOUND ) OR ( cur2%NOTFOUND ); + DBMS_OUTPUT.PUT_LINE( + 'No.' || v1_i_number || ': ' || v1_i_name ); + DBMS_OUTPUT.PUT_LINE( + 'No.' || v2_i_number || ': ' || v2_i_name ); + END LOOP; + CLOSE cur1; + CLOSE cur2; + END; + / +
+
+
DO $$ 
+ DECLARE 
+  cur1 CURSOR FOR 
+   SELECT i_number, i_name 
+    FROM inventory_table 
+    WHERE i_name = 'television'; 
+  cur2 CURSOR FOR 
+   SELECT i_number, i_name 
+    FROM inventory_table 
+    WHERE i_name = 'cd player'; 
+  v1_i_number inventory_table.i_number%TYPE; 
+  v2_i_number inventory_table.i_number%TYPE; 
+  v1_i_name inventory_table.i_name%TYPE; 
+  v2_i_name inventory_table.i_name%TYPE; 
+  flg1 BOOLEAN; 
+  flg2 BOOLEAN; 
+ BEGIN 
+  PERFORM DBMS_OUTPUT.SERVEROUTPUT( TRUE ); 
+  OPEN cur1; 
+  OPEN cur2; 
+  LOOP 
+   FETCH cur1 into v1_i_number, v1_i_name; 
+   flg1 := FOUND; 
+   FETCH cur2 into v2_i_number, v2_i_name; 
+   flg2 := FOUND; 
+   EXIT WHEN ( NOT flg1 ) OR ( NOT flg2 ); 
+   PERFORM DBMS_OUTPUT.PUT_LINE( 
+    'No.' || v1_i_number || ': ' || v1_i_name ); 
+   PERFORM DBMS_OUTPUT.PUT_LINE( 
+    'No.' || v2_i_number || ': ' || v2_i_name ); 
+  END LOOP; 
+  CLOSE cur1; 
+  CLOSE cur2; 
+ END; 
+ $$ 
+ ;
+
+ +**Note** + +---- + +Statements in which %NOTFOUND is determined to be NULL cannot be migrated. If SQL has not been executed at all, FOUND is set to FALSE, which is the same return value as when no row has been affected. + +---- + +##### 5.2.3.3 %ROWCOUNT + +**Description** + +%ROWCOUNT indicates the number of rows processed by an SQL statement. + +**Functional differences** + + - **Oracle database** + - %ROWCOUNT can be used. + - **PostgreSQL** + - %ROWCOUNT cannot be used. Use ROW_COUNT instead. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword %ROWCOUNT and identify where it is used. + 2. Declare the variable that will store the value obtained by ROW_COUNT. + 3. Use GET DIAGNOSTICS immediately in front of %ROWCOUNT identified in step 1. It obtains ROW_COUNT and stores its value in the variable declared in step 2. + 4. Replace the portion that calls %ROWCOUNT with the variable used in step 3. + +**Migration example** + +The example below shows migration when the number of deleted rows is obtained. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SET SERVEROUTPUT ON; 
+
+
+ BEGIN +
+ DELETE FROM inventory_table + WHERE i_name = 'television'; +
+ DBMS_OUTPUT.PUT_LINE ( + TO_CHAR( SQL%ROWCOUNT ) || 'rows deleted!' ); + END; + / +
+
+
DO $$ 
+ DECLARE 
+  row_num INTEGER; 
+ BEGIN 
+  PERFORM DBMS_OUTPUT.SERVEROUTPUT( TRUE ); 
+  DELETE FROM inventory_table 
+   WHERE i_name = 'television'; 
+  GET DIAGNOSTICS row_num := ROW_COUNT; 
+  PERFORM DBMS_OUTPUT.PUT_LINE( 
+   TO_CHAR( row_num ) || 'rows deleted!' ); 
+ END; 
+ $$ 
+ ;
+
+ +**Note** + +Statements in which %ROWCOUNT is determined to be NULL cannot be migrated. If SQL has not been executed at all, 0 is set. + +##### 5.2.3.4 REF CURSOR + +**Description** + +REF CURSOR is a data type that represents the cursor in Oracle databases. + +**Functional differences** + + - **Oracle database** + - REF CURSOR type variables can be defined. + - **PostgreSQL** + - REF CURSOR type variables cannot be defined. Use refcursor type variables instead. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword REF CURSOR and identify where it is used. + 2. Delete the REF CURSOR type definition and the portion where the cursor variable is declared using that type. + 3. Change the specification so that the cursor variable is declared using the refcursor type. + +**Migration example** + +The example below shows migration when the cursor variable is used to FETCH a row. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SET SERVEROUTPUT ON; 
+ DECLARE 
+  TYPE curtype IS REF CURSOR; 
+  cur curtype; 
+  v_inventory inventory_table%ROWTYPE; 
+ BEGIN 
+
+ OPEN cur FOR + SELECT * FROM inventory_table + WHERE i_warehouse = 2; + DBMS_OUTPUT.PUT_LINE( 'In warehouse no.2 is :' ); +
+ LOOP + FETCH cur into v_inventory; + EXIT WHEN cur%NOTFOUND; + DBMS_OUTPUT.PUT_LINE( + 'No.' || v_inventory.i_number || + ': ' || v_inventory.i_name || + '(' || v_inventory.i_quantity || ')' ); + END LOOP; + CLOSE cur; + END; + / +
+
+
DO $$ 
+ DECLARE 
+  cur refcursor; 
+
+ v_inventory inventory_table%ROWTYPE; + BEGIN + PERFORM DBMS_OUTPUT.SERVEROUTPUT(TRUE); + OPEN cur FOR + SELECT * FROM inventory_table + WHERE i_warehouse = 2; + PERFORM DBMS_OUTPUT.PUT_LINE( + 'In warehouse no.2 is :' ); + LOOP + FETCH cur into v_inventory; + EXIT WHEN NOT FOUND; + PERFORM DBMS_OUTPUT.PUT_LINE( + 'No.' || v_inventory.i_number || + ': ' || v_inventory.i_name || + '(' || v_inventory.i_quantity || ')' ); + END LOOP; + CLOSE cur; + END; + $$ + ;
+
+ +**Note** + +----- + +The RETURN clause (specifies the return type of the cursor itself) cannot be specified in the refcursor type provided by PostgreSQL. + +----- + +##### 5.2.3.5 FORALL + +**Description** + +FORALL uses the changing value of the VALUES clause or WHERE clause to execute a single command multiple times. + +**Functional differences** + + - **Oracle database** + - FORALL statements can be used. + - **PostgreSQL** + - FORALL statements cannot be used. + +**Migration procedure** + +FORALL statements cannot be used, so replace them with FOR statements so that the same result is returned. Use the following procedure to perform migration: + + 1. Search for the keyword FORALL and identify where it is used. + 2. Store the elements used by commands within FORALL in array type variables. In addition, delete Oracle database array definitions. + 3. Replace FORALL statements with FOR - LOOP statements. + 4. Replace portions that reference an array in the Oracle database with referencing of the array type variable defined in step 2. The portions changed in the migration example and details of the changes are as follows: + - Start of the loop: Change i_numL.FIRST to 1. + - End of the loop: Replace i_numL.LAST with ARRAY_LENGTH. + - Referencing of array elements: Change i_numL(i) to i_numL[i]. + +**Migration example** + +The example below shows migration when FORALL is used to execute INSERT. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 
+ DECLARE 
+   TYPE NumList IS TABLE OF SMALLINT; 
+   i_numL NumList := NumList( 151, 
+                              152, 
+                              153, 
+                              154, 
+                              155 ); 
+ BEGIN 
+   FORALL i IN i_numL.FIRST .. i_numL.LAST 
+     INSERT INTO inventory_table 
+     VALUES ( i_numL(i), 'television', 10, 2 ); 
+
+ END; + / +
+
+
DO $$ 
+ DECLARE 
+
+ i_numL SMALLINT ARRAY := '{ 151, + 152, + 153, + 154, + 155 }'; + BEGIN + FOR i IN 1..ARRAY_LENGTH( i_numL, 1 ) LOOP + INSERT INTO inventory_table + VALUES ( i_numL[i], 'television', 10, 2 ); + END LOOP; + END; + $$ + ;
+
+ +### 5.3 Migrating Functions + +This section explains how to migrate PL/SQL functions. + +**Description** + +A stored function is a user-defined function that returns a value. + +#### 5.3.1 Defining Functions + +**Functional differences** + + - **Oracle database** + - A RETURN clause within a function prototype is specified as RETURN.
DECLARE does not need to be specified as the definition portion of a variable used within a function. + - **PostgreSQL** + - Use RETURNS to specify a RETURN clause within a function prototype.
DECLARE must be specified as the definition portion of a variable to be used within a function. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keywords CREATE and FUNCTION, and identify where user-defined functions are created. + 2. If an IN or OUT qualifier is specified in an argument, move it to the beginning of the parameters. + 3. Change RETURN within the function prototype to RETURNS. + 4. Change the AS clause to AS $$. (If the keyword is IS, change it to AS.) + 5. If a variable is defined, add the DECLARE keyword after $$. + 6. Delete the final slash (/) and specify $$ and a LANGUAGE clause. + +**Migration example** + +The example below shows migration when CREATE FUNCTION is used to define a function. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
CREATE FUNCTION PROFIT_FUNC( 
+  selling IN INTEGER, 
+  sales_num IN INTEGER, 
+  cost IN INTEGER 
+ ) RETURN INTEGER AS 
+
+ profit INTEGER; + BEGIN + profit := ( ( selling * sales_num ) - cost ); + RETURN profit; + END; + /
+
+
CREATE FUNCTION PROFIT_FUNC( 
+  IN selling INTEGER, 
+  IN sales_num INTEGER, 
+  IN cost INTEGER 
+ ) RETURNS INTEGER AS $$ 
+ DECLARE 
+     profit INTEGER; 
+ BEGIN 
+     profit := ( ( selling * sales_num ) - cost ); 
+     RETURN profit; 
+ END; 
+ $$ LANGUAGE plpgsql;
+
+ +### 5.4 Migrating Procedures + +This section explains how to migrate PL/SQL procedures. + +**Description** + +A stored procedure is a single procedure into which multiple processes have been grouped. + +#### 5.4.1 Defining Procedures + +**Functional differences** + + - **Oracle database** + - Procedures can be created. + - **PostgreSQL** + - Procedures cannot be created. + +**Migration procedure** + +Procedures cannot be created in PostgreSQL. Therefore, replace them with functions. Use the following procedure to perform migration: + + 1. Search for the keywords CREATE and PROCEDURE, and identify where a procedure is defined. + 2. Replace the CREATE PROCEDURE statement with the CREATE FUNCTION statement. + 3. Change the AS clause to RETURNS VOID AS $$. (If the keyword is IS, change it to AS.) + 4. If a variable is defined, add the DECLARE keyword after $$. + 5. Delete the final slash (/) and specify $$ and a LANGUAGE clause. + +**Note** + +---- + +If the OUT or INOUT keywords are specified in the arguments, a different migration method must be used. Refer to "Defining Procedures That Return a Value". + +---- + +**Migration example** + +The example below shows migration when a procedure is defined. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
CREATE PROCEDURE UPD_QUANTITY ( 
+  upd_number SMALLINT, 
+  upd_quantity INTEGER 
+ ) AS 
+   BEGIN 
+     UPDATE inventory_table 
+ SET i_quantity = upd_quantity 
+       WHERE i_number = upd_number; 
+   END; 
+ / 
+ ------------------------------------------------- 
+
+ DECLARE + v_i_number SMALLINT := 110; + v_i_quantity INTEGER := 100; + BEGIN + upd_quantity( v_i_number, v_i_quantity ); + END; + / +
+
+
CREATE FUNCTION UPD_QUANTITY ( 
+  upd_number SMALLINT, 
+  upd_quantity INTEGER 
+ ) RETURNS VOID AS $$ 
+ BEGIN 
+     UPDATE inventory_table 
+ SET i_quantity = upd_quantity 
+       WHERE i_number = upd_number; 
+ END; 
+ $$ LANGUAGE plpgsql; 
+ ------------------------------------------------- 
+ DO $$ 
+ DECLARE 
+   v_i_number SMALLINT := 110; 
+   v_i_quantity INTEGER := 100; 
+ BEGIN 
+   PERFORM upd_quantity( v_i_number, v_i_quantity ); 
+ END; 
+ $$ 
+ ;
+
+ +#### 5.4.2 Calling Procedures + +**Functional differences** + + - **Oracle database** + - A procedure can be called as a statement. + - **PostgreSQL** + - Procedures cannot be used. Instead, call the procedure as a function that does not return a value. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Identify where each procedure is called. + 2. Specify PERFORM in front of the procedure call. + +**Migration example** + +The example below shows migration when a procedure is called. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SET SERVEROUTPUT ON; 
+ BEGIN 
+
+ DBMS_OUTPUT.PUT_LINE( 'Hello World.' ); + END; + / +
+
+
DO $$ 
+ BEGIN 
+   PERFORM DBMS_OUTPUT.SERVEROUTPUT( TRUE ); 
+   PERFORM DBMS_OUTPUT.PUT_LINE( 'Hello World.' ); 
+ END; 
+ $$ 
+ ;
+
+ +#### 5.4.3 Defining Procedures That Return a Value + +**Functional differences** + + - **Oracle database** + - Procedures that return a value can be created. + - **PostgreSQL** + - Procedures that return a value cannot be created. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the CREATE and PROCEDURE keywords, and identify where a procedure is defined. + 2. Confirm that the OUT or INOUT keyword is specified in the arguments. + 3. Replace the CREATE PROCEDURE statement with the CREATE FUNCTION statement. + 4. If the IN, OUT, or INOUT keyword is specified in the arguments, move it to the beginning of the arguments. + 5. Change the AS clause to AS $$. (If the keyword is IS, change it to AS.) + 6. If a variable is defined, add the DECLARE keyword after $$. + 7. Delete the final slash (/) and specify $$ and a LANGUAGE clause. + 8. If calling a function, call it without specifying arguments in the OUT parameter and store the return value in the variable. If there are multiple OUT parameters, use a SELECT INTO statement. + +**Migration example** + +The example below shows migration when the OUT parameter of CREATE PROCEDURE is used to define a procedure that returns a value. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 CREATE PROCEDURE remove_row ( 
+  del_name VARCHAR2, 
+  del_row OUT INTEGER 
+ ) AS 
+  BEGIN 
+   DELETE FROM inventory_table 
+    WHERE i_name = del_name; 
+   del_row := SQL%ROWCOUNT; 
+  END; 
+ / 
+ ------------------------------------------------- 
+ SET SERVEROUTPUT ON; 
+ DECLARE 
+  rtn_row INTEGER; 
+  v_i_name VARCHAR2(20) := 'television'; 
+ BEGIN 
+
+ remove_row( v_i_name, rtn_row ); + DBMS_OUTPUT.PUT_LINE( + TO_CHAR( rtn_row ) || 'rows deleted!' ); + END; + / +
+
+
CREATE FUNCTION remove_row ( 
+  del_name VARCHAR, 
+  OUT del_row INTEGER 
+ ) AS $$ 
+  BEGIN 
+   DELETE FROM inventory_table 
+    WHERE i_name = del_name; 
+   GET DIAGNOSTICS del_row := ROW_COUNT; 
+  END; 
+ $$ LANGUAGE plpgsql; 
+ ------------------------------------------------- 
+ DO $$ 
+ DECLARE 
+  rtn_row INTEGER; 
+  v_i_name VARCHAR(20) := 'television'; 
+ BEGIN 
+  PERFORM DBMS_OUTPUT.SERVEROUTPUT( TRUE ); 
+  rtn_row := remove_row( v_i_name ); 
+  PERFORM DBMS_OUTPUT.PUT_LINE( 
+   TO_CHAR( rtn_row ) || 'rows deleted!' ); 
+ END; 
+ $$ 
+ ;
+
+ + +**See** + +---- + +Refer to "Defining Nested Procedures" for examples of migrating a call portion that uses a SELECT INTO statement. + +---- + +#### 5.4.4 Defining Nested Procedures + +**Functional differences** + + - **Oracle database** + - Nested procedures can be defined. + - **PostgreSQL** + - Nested procedures cannot be defined. + +**Migration procedure** + +Procedures must be replaced with functions, but functions cannot be nested in PostgreSQL. Therefore, define and call the functions separately. Use the following procedure to perform migration: + + 1. Search for the CREATE and PROCEDURE keywords, and identify where a procedure is defined. + 2. If a PROCEDURE statement is defined in a DECLARE clause, regard it as a nested procedure. + 3. Check for variables that are used by both the procedure and the nested procedure. + 4. Replace a nested procedure (from PROCEDURE procedureName to END procedureName;) with a CREATE FUNCTION statement. Specify the variables you found in step 3 in the INOUT argument of CREATE FUNCTION. + 5. Replace the portion that calls the nested procedure with a SELECT INTO statement. Specify the common variables you found in step 3 in both the variables used for calling the function and the variables used for accepting the INTO clause. + +**Migration example** + +The example below shows migration when nested procedures are used and a variable is shared by a procedure and its call portion. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SET SERVEROUTPUT ON; 
+ DECLARE 
+  sales_num INTEGER; 
+  stock_num INTEGER; 
+  v_i_quantity INTEGER; 
+  PROCEDURE quantity_check ( 
+   sales INTEGER, 
+   stock INTEGER 
+  ) IS 
+   quantity_err EXCEPTION; 
+  BEGIN 
+
+ v_i_quantity := ( stock - sales ); + IF ( v_i_quantity < 0 ) THEN + RAISE quantity_err; + END IF; + EXCEPTION + WHEN quantity_err THEN + DBMS_OUTPUT.PUT_LINE( + 'ERR: i_quantity is negative value.' ); + END quantity_check; +
+
+
+
+ BEGIN +
+ sales_num := 80; + stock_num := 100; + quantity_check( sales_num, stock_num ); +
+ DBMS_OUTPUT.PUT_LINE( + 'i_quantity: ' || v_i_quantity ); +
+ sales_num := 100; + stock_num := 80; + quantity_check( sales_num, stock_num ); +
+ DBMS_OUTPUT.PUT_LINE( + 'i_quantity: ' || v_i_quantity ); + END; + / +
+
+
+
+
+
+
 
+ CREATE FUNCTION quantity_check( 
+  sales INTEGER, 
+  stock INTEGER, 
+  INOUT quantity INTEGER 
+ ) AS $$ 
+
+ BEGIN + PERFORM DBMS_OUTPUT.SERVEROUTPUT( TRUE ); + quantity := ( stock - sales ); + IF ( quantity < 0 ) THEN + RAISE USING ERRCODE = '20001'; + END IF; + EXCEPTION + WHEN SQLSTATE '20001' THEN + PERFORM DBMS_OUTPUT.PUT_LINE( + 'ERR: i_quantity is negative value.' ); + END; + $$ LANGUAGE plpgsql; + ------------------------------------------------- + DO $$ + DECLARE + sales_num INTEGER; + stock_num INTEGER; + v_i_quantity INTEGER; + BEGIN + PERFORM DBMS_OUTPUT.SERVEROUTPUT( TRUE ); + sales_num := 80; + stock_num := 100; + SELECT quantity INTO v_i_quantity + FROM quantity_check( sales_num, + stock_num, + v_i_quantity ); + PERFORM DBMS_OUTPUT.PUT_LINE( + 'i_quantity: ' || v_i_quantity ); +
+ sales_num := 100; + stock_num := 80; + SELECT quantity INTO v_i_quantity + FROM quantity_check( sales_num, + stock_num, + v_i_quantity ); + PERFORM DBMS_OUTPUT.PUT_LINE( + 'i_quantity: ' || v_i_quantity ); + END; + $$ + ;
+
+ +#### 5.4.5 Defining Anonymous Code Blocks + +**Description** + +An anonymous code block generates and executes a temporary function within a procedural language. + +**Functional differences** + + - **Oracle database** + - Anonymous code blocks that are enclosed with (DECLARE) BEGIN to END can be executed. + - **PostgreSQL** + - PL/pgSQL blocks ((DECLARE) BEGIN to END) that are enclosed with DO $$ to $$ can be executed. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keywords DECLARE and BEGIN, and identify where an anonymous code block is defined. + 2. Specify DO $$ at the beginning of the anonymous code block. + 3. Delete the final slash (/) and specify $$. + +**Migration example** + +The example below shows migration when an anonymous code block is defined. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SET SERVEROUTPUT ON; 
+ BEGIN 
+
+ DBMS_OUTPUT.PUT_LINE( 'Hello World.' ); + END; + / +
+
+
DO $$ 
+ BEGIN 
+     PERFORM DBMS_OUTPUT.SERVEROUTPUT( TRUE ); 
+     PERFORM DBMS_OUTPUT.PUT_LINE( 'Hello World.' ); 
+ END; 
+ $$ 
+ ;
+
+ +### 5.5 Migrating Packages + +This section explains how to migrate PL/SQL packages. + +**Description** + +A package defines and contains procedures and functions as a single relationship group in the database. + +**Functional differences** + + - **Oracle database** + - Packages can be created. + - **PostgreSQL** + - Packages cannot be created. + +Packages cannot be created in PostgreSQL, so define a schema with the same name as the package and define functions that have a relationship in the schema so that they are treated as a single group. +In the following sections, the migration procedure is explained for each feature to be defined in a package. + +#### 5.5.1 Defining Functions Within a Package + +**Functional differences** + + - **Oracle database** + - Functions can be created within a package. + - **PostgreSQL** + - The package itself cannot be created. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keywords CREATE and PACKAGE, and identify where they are defined. + 2. Define a schema with the same name as the package. + 3. If a FUNCTION statement is specified within a CREATE PACKAGE BODY statement, define, within the schema created in step 2, the functions that were defined within the package. + +**Migration example** + +The example below shows migration when a package is defined and functions are created within that package. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
CREATE PACKAGE smpl_pkg AS 
+   FUNCTION remove_row( rm_i_name VARCHAR2 ) 
+     RETURN INTEGER; 
+ END smpl_pkg; 
+ / 
+ CREATE PACKAGE BODY smpl_pkg AS 
+   FUNCTION remove_row( rm_i_name VARCHAR2 ) 
+     RETURN INTEGER IS 
+
+ rtn_row INTEGER; + BEGIN + DELETE FROM inventory_table + WHERE i_name = rm_i_name; +
+ RETURN(SQL%ROWCOUNT); + END; + END smpl_pkg; + /
+
+
CREATE SCHEMA smpl_scm; 
+
+
+
+
+ CREATE FUNCTION smpl_scm.remove_row( + rm_i_name VARCHAR + ) RETURNS INTEGER AS $$ + DECLARE + rtn_row INTEGER; + BEGIN + DELETE FROM inventory_table + WHERE i_name = rm_i_name; + GET DIAGNOSTICS rtn_row := ROW_COUNT; + RETURN rtn_row; + END; + $$ LANGUAGE plpgsql; +
+
+ +**See** + +---- + +Refer to "Defining Functions" for information on migrating FUNCTION statements within a package. + +---- + +#### 5.5.2 Defining Procedures Within a Package + +**Functional differences** + + - **Oracle database** + - Procedures can be created within a package. + - **PostgreSQL** + - The package itself cannot be created. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keywords CREATE and PACKAGE, and identify where they are defined. + 2. Define a schema with the same name as the package. + 3. If a PROCEDURE statement is specified within a CREATE PACKAGE BODY statement, migrate the procedures that were defined within the package to functions and define them within the schema created in step 2. + +**Migration example** + +The example below shows migration when a package is defined and procedures are created within that package. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
CREATE PACKAGE smpl_pkg AS 
+  PROCEDURE increase_row( 
+   add_i_num SMALLINT, 
+   add_i_name VARCHAR2, 
+   add_i_quantity INTEGER, 
+   add_i_warehouse SMALLINT 
+  ); 
+  END smpl_pkg; 
+ / 
+ CREATE PACKAGE BODY smpl_pkg AS 
+  PROCEDURE increase_row( 
+   add_i_num SMALLINT, 
+   add_i_name VARCHAR2, 
+   add_i_quantity INTEGER, 
+   add_i_warehouse SMALLINT 
+  ) IS 
+   BEGIN 
+    INSERT INTO inventory_table 
+     VALUES ( add_i_num, 
+              add_i_name, 
+              add_i_quantity, 
+              add_i_warehouse ); 
+     END; 
+ END smpl_pkg; 
+ /
+
+
CREATE SCHEMA smpl_scm; 
+
+
+
+
+
+
+
+
+
+ CREATE FUNCTION smpl_scm.increase_row( + add_i_num SMALLINT, + add_i_name VARCHAR, + add_i_quantity INTEGER, + add_i_warehouse SMALLINT + ) RETURNS VOID AS $$ + BEGIN + INSERT INTO inventory_table + VALUES ( add_i_num, + add_i_name, + add_i_quantity, + add_i_warehouse ); + END; + $$ LANGUAGE plpgsql; +
+
+ +**See** + +---- + +Refer to "Defining Procedures" for information on migrating PROCEDURE statements within a package. + +---- + +#### 5.5.3 Sharing Variables Within a Package + +**Functional differences** + + - **Oracle database** + - Variables can be shared within a package. + - **PostgreSQL** + - A package cannot be created, so variables cannot be shared. + +**Migration procedure** + +Use a temporary table instead of variables within a package. Use the following procedure to perform migration: + + 1. Search for the keywords CREATE and PACKAGE, and identify where they are defined. + 2. Check for variables defined directly in a package. + 3. Create a temporary table that defines the variables checked in step 2 in a column. + 4. Insert one record to the temporary table created in step 3. (The set value is the initial value specified within the package.) + 5. Replace the respective portions that reference a variable and set a variable with SQL statements. + - To reference a variable, use a SELECT INTO statement to store a value in the variable and then reference it. (A variable for referencing a variable must be defined separately.) + - To update a variable, use an UPDATE statement and update the target column. + +**Migration example** + +The example below shows migration when a package is defined and variables within the package are shared. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SET SERVEROUTPUT ON; 
+ CREATE PACKAGE row_pkg AS 
+   PROCEDURE set_item( item INTEGER ); 
+   i_item INTEGER; 
+ END row_pkg; 
+ / 
+ CREATE PACKAGE BODY row_pkg AS 
+
+ PROCEDURE set_item( + item INTEGER + ) IS + BEGIN + i_item := item; + END; + END row_pkg; + / + ------------------------------------------------- + SET SERVEROUTPUT ON; +
+
+
+
+
+
+ BEGIN +
+ row_pkg.set_item( 1000 ); +
+
+ DBMS_OUTPUT.PUT_LINE( + 'ITEM :' || row_pkg.i_item ); + row_pkg.set_item(2000); +
+
+ DBMS_OUTPUT.PUT_LINE( + 'ITEM :' || row_pkg.i_item ); + END; + / +
+
+
CREATE SCHEMA row_pkg; 
+
+
+
+
+
+
+
+ CREATE FUNCTION row_pkg.set_item( + item INTEGER + ) RETURNS VOID AS $$ + BEGIN + UPDATE row_pkg_variables SET i_item = item; + END; + $$ LANGUAGE plpgsql; +
+ ------------------------------------------------- + CREATE TEMP TABLE row_pkg_variables ( i_item INTEGER ); + INSERT INTO row_pkg_variables VALUES (0); +
+ DO $$ + DECLARE + g_item INTEGER; + BEGIN + PERFORM DBMS_OUTPUT.SERVEROUTPUT( TRUE ); + PERFORM row_pkg.set_item( 1000 ); + SELECT i_item INTO g_item + FROM row_pkg_variables; + PERFORM DBMS_OUTPUT.PUT_LINE( + 'ITEM :' || g_item ); + PERFORM row_pkg.set_item(2000); + SELECT i_item INTO g_item + FROM row_pkg_variables; + PERFORM DBMS_OUTPUT.PUT_LINE( + 'ITEM :' || g_item ); + END; + $$ + ;
+
+ diff --git a/contrib/orafce/doc/sql_migration/sql_migration06.md b/contrib/orafce/doc/sql_migration/sql_migration06.md new file mode 100644 index 000000000..63499c635 --- /dev/null +++ b/contrib/orafce/doc/sql_migration/sql_migration06.md @@ -0,0 +1,2605 @@ +Chapter 6 Notes on Using orafce +--- + +This chapter provides notes on using Oracle database compatibility features added by orafce. + +### 6.1 Data Types +This section explains how to migrate data types added by orafce. + +#### 6.1.1 Notes on VARCHAR2 +This section provides notes on VARCHAR2. + +##### 6.1.1.1 Specifying the Maximum Number of Bytes and Maximum Number of Characters + +**Functional differences** + + - **Oracle database** + - Specifying the keyword BYTE or CHAR after a size enables the size to be indicated in terms of the maximum number of bytes or the maximum number of characters. + - **PostgreSQL** + - The keyword BYTE or CHAR cannot be set after the size. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword VARCHAR2 and check if the keyword BYTE or CHAR is specified after the size. + 2. If the BYTE keyword is specified, delete it. + 3. If the CHAR keyword is specified, delete it and convert the data type to VARCHAR. + +**Migration example** + +The example below shows migration when the maximum number of bytes or the maximum number of characters for the VARCHAR2 type is specified. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
CREATE TABLE t1( 
+  col1 VARCHAR2(5 BYTE), 
+  col2 VARCHAR2(5 CHAR) 
+ );
+
+
CREATE TABLE t1( 
+  col1 VARCHAR2(5), 
+  col2 VARCHAR(5) 
+ );
+
+ + +**Note** + +---- + +The VARCHAR2 type does not support collating sequences. Therefore, the following error occurs when a collating sequence like that of an ORDER BY clause is required. At this time, the following HINT will prompt to use a COLLATE clause, however, because collating sequences are not supported, it is not possible to use this clause. + +~~~ +ERROR: could not determine which collation to use for string comparison +HINT: Use the COLLATE clause to set the collation explicitly. + +~~~ + +If the error shown above is displayed, explicitly cast the column to VARCHAR or TEXT type. + +---- + +### 6.2 Functions +This section explains how to migrate functions added by orafce. + +#### 6.2.1 INSTRB +**Description** + +INSTRB searches for a substring in a string and returns the start position (in bytes) of the first occurrence of the substring. +##### 6.2.1.1 Obtaining the Start Position of a Substring (in Bytes) +**Functional differences** + + - **Oracle database** + - INSTRB searches for a substring in a string and returns the start position (in bytes) of the substring. + - **PostgreSQL** + - There is no INSTRB function. Use STRPOSB instead. STRPOSB is unique to orafce. + +**Migration procedure** + +Use the following procedure to migrate to STRPOSB: + + 1. Search for the keyword INSTRB and identify where it is used. + 2. Confirm that arguments up to the second argument are specified. + 3. Change INSTRB to STRPOSB. + +**Migration example** + +The example below shows migration when searching for a particular substring in a string, and returning the start position of the substring in bytes. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SELECT c_code, INSTRB( c_address, ',' ) 
+  FROM company_table;
+
+
SELECT c_code, STRPOSB( c_address, ',' ) 
+  FROM company_table;
+
+ + +**Note** + +---- + +If the third argument is specified in INSTRB, refer to the conversion example shown below. If the fourth argument is specified, migration is not possible. + +---- + +**Information** + +---- + +The general rules for STRPOSB are as follows: + +---- + +**Description** + +INSTRB returns the start position (in bytes) of a substring within a string. + +**Specification format** + +![STRPOSB](gif/STRPOSB.gif) + +**General rules** + + - STRPOSB searches for string *str2* in *str1* and returns the start position it finds in bytes. + - If *str2* is not found, 0 is returned. + - The data type of the return value is INTEGER. + +##### 6.2.1.2 Obtaining the Start Position of a Substring from a Specified Search Start Position (in Bytes) + +**Functional differences** + + - **Oracle database** + - The search start position is specified in the third argument of INSTRB. + - **PostgreSQL** + - A search start position cannot be specified with STRPOSB. + +**Migration procedure** + +A search start position cannot be specified, so truncate the search target string to the start position so that the same result is returned. Use the following procedure to perform migration: + + 1. Search for the keyword INSTRB and identify where it is used. + 2. Confirm that arguments up to the third argument are specified and that a positive number is specified. + 3. Enclose the string specified in the first argument with SUBSTRB, and specify the value specified in the third argument of INSTRB as the second argument of SUBSTRB. + 4. Change INSTRB to STRPOSB and delete the value specified in the third argument. + 5. Enclose the function in a simple CASE expression to evaluate the result of the function changed in step 4.
Define the selector so that 0 is returned when the result is 0.
If the result is not 0, specify the same function as in step 4, and add the value obtained by subtracting 1 from the value specified in the second argument of SUBSTRB. + +**Migration example** + +The example below shows migration when a search start position is specified and then the start position of a string is found in bytes. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SELECT c_code, INSTRB( c_address, '-', 10 ) 
+ FROM company_table; 
+
+
+
+
+
+
+
SELECT c_code, 
+ CASE STRPOSB( SUBSTRB( c_address, 10 ),'-') 
+ WHEN 0 THEN 0 
+ ELSE STRPOSB( SUBSTRB( c_address, 10 ), '-' ) + 9 
+ END 
+ FROM company_table;
+
+ + + +#### 6.2.2 INSTRC, INSTR2, and INSTR4 + +**Description** + +INSTRC, INSTR2, and INSTR4 return the start position of a substring in a string using the relevant encoding. + +**Functional differences** + + - **Oracle database** + - INSTRC, INSTR2, and INSTR4 use the relevant encoding to search for a substring in a string from a specified position and then return the start position of the substring. + - **PostgreSQL** + - There are no INSTRC, INSTR2, and INSTR4 functions. Only Unicode encoding is used in PostgreSQL. + +**Migration procedure** + +Use the following procedure to migrate to INSTR: + + 1. Search for the keywords INSTRC, INSTR2, and INSTR4, and identify where they are used. + 2. Change those keywords to INSTR. + +**Migration example** + +The example below shows migration from INSTRC, INSTR2, and INSTR4. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 SELECT c_name, INSTRC( c_name, 'Corp', 2, 1 ) 
+  FROM company_table; 
+
+ SELECT c_name, INSTR2( c_name, 'Corp', 2, 1 ) + FROM company_table; +
+ SELECT c_name, INSTR4( c_name, 'Corp', 2, 1 ) + FROM company_table;
+
+
SELECT c_name, INSTR( c_name, 'Corp', 2, 1 ) 
+  FROM company_table; 
+
+
+
+
+
+
+
+
+ + +#### 6.2.3 LENGTHC, LENGTH2, and LENGTH4 + +**Description** + +LENGTHC, LENGTH2, and LENGTH4 use the relevant encoding to return the length of the specified string. + +**Functional differences** + + - **Oracle database** + - LENGTHC, LENGTH2, and LENGTH4 use the relevant encoding to return the length of the specified string. + - **PostgreSQL** + - There are no LENGTHC, LENGTH2, and LENGTH4 functions. Only Unicode encoding is used in PostgreSQL. + +**Migration procedure** + +Use the following procedure to migrate to LENGTH: + + 1. Search for the keywords LENGTHC, LENGTH2, and LENGTH4, and identify where they are used. + 2. Change those keywords to LENGTH. + +**Migration example** + +The example below shows migration from LENGTHC, LENGTH2, and LENGTH4. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 SELECT name, LENGTHC( name ) 
+  FROM staff_table 
+  WHERE job = 'sales member'; 
+
+ SELECT name, LENGTH2( name ) + FROM staff_table + WHERE job = 'sales member'; +
+ SELECT name, LENGTH4( name ) + FROM staff_table + WHERE job = 'sales member';
+
+
 SELECT name, LENGTH( name ) 
+  FROM staff_table 
+  WHERE job = 'sales member'; 
+
+
+
+
+
+
+
+
+
+
+ + + +#### 6.2.4 LISTAGG + +**Description** + +LISTAGG returns a concatenated, delimited list of string values. + +##### 6.2.4.1 Specifying the Join Sequence for a List + +**Functional differences** + + - **Oracle database** + - The join sequence for a list is specified using WITHIN GROUP(ORDER BY). + - **PostgreSQL** + - WITHIN GROUP(ORDER BY) cannot be used. Instead, a join sequence can be specified using ORDER BY immediately after the value. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword LISTAGG and confirm where it is used. + 2. Move the ORDER BY clause of WITHIN GROUP(ORDER BY) immediately after the value of LISTAGG and then delete WITHIN GROUP(). + +**Migration example** + +The example below shows migration of the join sequence of specified values. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 SELECT manager_id, 
+        LISTAGG( name, ', ' ) 
+         WITHIN GROUP( ORDER BY staff_id ) 
+  FROM staff_table 
+  GROUP BY manager_id;
+
+
 SELECT manager_id, 
+        LISTAGG( name, ', ' ORDER BY staff_id ) 
+
+ FROM staff_table + GROUP BY manager_id;
+
+ + +##### 6.2.4.2 Specifying the Join Sequence for a List per Group (Window Functions) + +**Functional differences** + + - **Oracle database** + - The join sequence for a list per group is specified using WITHIN GROUP(ORDER BY) OVER(PARTITION BY). + - **PostgreSQL** + - The join sequence for a list per group cannot be specified. + +**Migration procedure** + +The join sequence for a list per group cannot be specified, so sort the data into the sequence in which it is to be joined and then join it. Use the following procedure to perform migration: + + 1. Search for the keywords LISTAGG and OVER, and identify where the OVER clause of LISTAGG is used. + 2. Convert the table in the FROM clause to a subquery, and move the ORDER BY clause of WITHIN GROUP(ORDER BY) to the subquery. + 3. Delete WITHIN GROUP(ORDER BY). + +**Migration example** + +The example below shows migration when a join sequence for a list per group is specified. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 SELECT name, 
+        manager_id, 
+        LISTAGG( name, ', ' ) 
+         WITHIN GROUP( ORDER BY staff_id ) 
+         OVER( PARTITION BY manager_id )  
+  FROM staff_table; 
+
+
+
+
 SELECT name, 
+        manager_id, 
+        LISTAGG( name, ', ' ) 
+
+ OVER( PARTITION BY manager_id ) + FROM ( SELECT * FROM staff_table + ORDER BY staff_id ) st_tbl; +
+
+ + +#### 6.2.5 NLSSORT +**Description** + +NLSSORT returns a binary value that denotes the lexical order of the locale (COLLATE). + +##### 6.2.5.1 Sorting by the Specified Locale + +**Functional differences** + + - **Oracle database** + - The locale is specified by NLS_SORT=locale.
The specifiable locales are provided by the Oracle database. + - **PostgreSQL** + - The locale is specified by locale.
The specifiable locales depend on the operating system. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword NLSSORT and identify where it is used. + 2. Delete NLS_SORT= and change the locale to the locale used by the operating system corresponding to the specified collating sequence. + +**Migration example** + +The example below shows migration when the specified locale is used for sorting. Note that the example locale in PostgreSQL would be the value specified for Linux. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 SELECT c_code, c_name 
+  FROM company_table 
+  ORDER BY NLSSORT( c_name, 
+                    'NLS_SORT = xDanish' ); 
+
+ SELECT c_code, c_name + FROM company_table + ORDER BY NLSSORT( c_name, + 'NLS_SORT = JAPANESE_M' );
+
+
 SELECT c_code, c_name 
+  FROM company_table 
+  ORDER BY NLSSORT( c_name, 'danish' ); 
+
+
+ SELECT c_code, c_name + FROM company_table + ORDER BY NLSSORT( c_name, 'ja_JP.UTF8' ); +
+
+ + + +##### 6.2.5.2 Sorting by Character Set + +**Functional differences** + + - **Oracle database** + - NLS_SORT=BINARY is specified in the locale specification for sorting by character set. + - **PostgreSQL** + - C is specified in the locale specification for sorting by character set. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword NLSSORT and identify where it is used. + 2. If NLS_SORT=BINARY is specified for the locale, change it to C. + +**Migration example** + +The example below shows migration when the character set is used for sorting. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 SELECT c_code, c_name 
+  FROM company_table 
+  ORDER BY NLSSORT( c_name, 'NLS_SORT = BINARY' );
+
+
 SELECT c_code, c_name 
+  FROM company_table 
+  ORDER BY NLSSORT( c_name, 'C' );
+
+ + + +##### 6.2.5.3 Case-Insensitive Sorting + +**Functional differences** + + - **Oracle database** + - Specifying _CI at the end of the locale sets case-insensitive sorting. + - **PostgreSQL** + - _CI cannot be specified at the end of the locale. + +**Migration procedure** + +There are no features that perform case-insensitive sorting, so make all characters either uppercase or lowercase before starting sorting so that the same result is returned. Use the following procedure to perform migration: + + 1. Search for the keyword NLSSORT and identify where it is used. + 2. If _CI is specified at the end of the specified locale, put the sort column inside the parentheses of LOWER (or UPPER). + +**Migration example** + +The example below shows migration when case-insensitive sorting is used. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 SELECT c_code, c_name 
+  FROM company_table 
+ ORDER BY NLSSORT( c_name, 
+                   'NLS_SORT = JAPANESE_M_CI' );
+
+
 SELECT c_code, c_name 
+  FROM company_table 
+  ORDER BY NLSSORT( LOWER( c_name ), 
+                    'ja_JP.UTF8' ); 
+ 
+
+ +#### 6.2.6 SUBSTRC, SUBSTR2, and SUBSTR4 + +**Description** + +SUBSTRC, SUBSTR2, and SUBSTR4 extract part of a string in the character unit of the relevant encoding. + +**Functional differences** + + - **Oracle database** + - SUBSTRC, SUBSTR2, and SUBSTR4 extract part of a string in the character unit of the relevant encoding. + - **PostgreSQL** + - There are no SUBSTRC, SUBSTR2, and SUBSTR4 functions. Only Unicode encoding is used in PostgreSQL. + +**Migration procedure** + +Use the following procedure to migrate to SUBSTR: + + 1. Search for the keywords SUBSTRC, SUBSTR2, and SUBSTR4, and identify where they are used. + 2. Change those keywords to SUBSTR. + +**Migration example** + +The example below shows migration when part of a string is extracted in the character unit of the relevant encoding. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 SELECT SUBSTRC( c_telephone, 5, 8 ) 
+  FROM company_table; 
+
+ SELECT SUBSTR2( c_telephone, 5, 8 ) + FROM company_table; +
+ SELECT SUBSTR4( c_telephone, 5, 8 ) + FROM company_table;
+
+
 SELECT SUBSTR( c_telephone, 5, 8 ) 
+  FROM company_table; 
+
+
+
+
+
+
+
+
+ + + +#### 6.2.7 SUBSTRB +**Description** + +SUBSTRB extracts part of a string in bytes. + +##### 6.2.7.1 Specifying Zero as the Start Position +**Functional differences** + + - **Oracle database** + - If 0 is specified as the start position, the part of the string is extracted from the first byte. + - **PostgreSQL** + - If 0 is specified as the start position, extraction starts at the position found by subtracting 1 from the start position and shifting by that number of positions to the left. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword SUBSTRB and identify where it is used. + 2. If 0 is specified as the start position, change it to 1. + +**Migration example** + +The example below shows migration when 0 is specified as the start position for SUBSTRB. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 SELECT SUBSTRB( c_telephone, 0, 7 ) || '-xxxx' 
+  FROM company_table;
+
+
 SELECT SUBSTRB( c_telephone, 1, 7 ) || '-xxxx' 
+  FROM company_table;
+
+ + +##### 6.2.7.2 Specifying a Negative Value as the Start Position +**Functional differences** + + - **Oracle database** + - If a negative value is specified as the start position, extraction starts at the position found by counting by that number of bytes after the end of the string. + - **PostgreSQL** + - If a negative value is specified as the start position, extraction starts at the position found by subtracting 1 from the start position and shifting by that number of positions to the left. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword SUBSTRB and identify where it is used. + 2. If a negative value is specified as the start position, add (OCTET_LENGTH(firstArgumentOfSubstrb)+1) before the negative value of the start position parameter. + +**Migration example** + +The example below shows migration when a negative value is specified as the start position for SUBSTRB. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 SELECT 'xxx-' || 
+        SUBSTRB( c_telephone, -8, 3 ) || 
+        '-xxxx' 
+ FROM company_table; 
+
+
+
+
+
+
SELECT 'xxx-' || 
+        SUBSTRB( c_telephone, 
+                 ( OCTET_LENGTH( c_telephone )  
+                    +1 ) -8, 
+                   3 ) || 
+        '-xxxx' 
+  FROM company_table;
+
+ +##### 6.2.7.3 Specifying a Value Less Than One as the String Length +**Functional differences** + + - **Oracle database** + - If a value less than 1 is specified as the string length, NULL is returned. + - **PostgreSQL** + - If the string length is 0, a null character is returned. A negative value cannot be specified as a string length. + +**Migration procedure** + +Use the following procedure to perform migration. Note that the final step depends on whether NULL or a null character is expected as the return value. + + - When expecting NULL as the return value + 1. Search for the keyword SUBSTRB and identify where it is used. + 2. Confirm that a value less than 1 is specified in the string length parameter. + 3. Change the string length to NULL. + - When expecting a null character as the return value + 1. Search for the keyword SUBSTRB and identify where it is used. + 2. Confirm that a value less than 1 is specified in the string length parameter. + 3. If a value less than 0 is specified as the string length, change it to 0. + +**Migration example** + +The example below shows migration when a value less than 1 is specified as the string length in SUBSTRB. In this example, NULL is expected as the return value. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 SELECT SUBSTRB( c_telephone, 1, -1 ) 
+  FROM company_table;
+
+
 SELECT SUBSTRB( c_telephone, 1, NULL ) 
+  FROM company_table;
+
+ +#### 6.2.8 TO_CHAR and TO_DATE + +**Description** + +TO_CHAR and TO_DATE convert the specified value in accordance with the format. + +##### 6.2.8.1 When Only Part of the TO_DATE Datetime Format is Specified + +**Functional differences** + + - **Oracle database** + - If only part of the TO_DATE datetime format is specified, the omitted portion is set automatically, with the year set to the current year, the month set to the current month, the day set to 1, and the hour, minute, and second set to 0. + - **PostgreSQL** + - If only part of the TO_DATE datetime format is specified, the omitted portion is set automatically, with the year, month, and day set to 1, and the hour, minute, and second set to 0. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword TO_DATE and confirm that the year or month is not specified in the datetime format. + 2. Use DATE_TRANC to find the year. If the year is omitted, specify SYSDATE to obtain the current year. + 3. Multiply the result of DATE_PART by one month indicated in the INTERVAL type to find the month. If the month is omitted, specify SYSDATE to obtain the current month. + 4. Add the results found in steps 2 and 3. + +**Migration example** + +The example below shows migration when only part of the TO_DATE datetime format is specified. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 SELECT TO_DATE( '04', 'MM' ) 
+  FROM DUAL;
+
+
+
+ SELECT TO_DATE( '2000', 'YYYY' ) + FROM DUAL; +
+
+
+
+
 SELECT DATE_TRUNC( 'YEAR', SYSDATE() ) 
+ + ( DATE_PART( 'MONTH', TO_DATE( '04', 'MM' ) ) - 1 ) 
+ * INTERVAL '1 MONTH' 
+ FROM DUAL; 
+
+ SELECT DATE_TRUNC( 'YEAR', TO_DATE( '2000', 'YYYY' ) ) + + ( DATE_PART( 'MONTH', SYSDATE() ) - 1 ) + * INTERVAL '1 MONTH' + FROM DUAL;
+
+ + + +##### 6.2.8.2 Omitting the Data Type Format + +**Functional differences** + + - **Oracle database** + - If the data type format (datetime format) is omitted from TO_DATE or TO_CHAR, the values are converted in accordance with NLS_DATE_FORMAT.
Statements such as ALTER SESSION can be used to change NLS_DATE_FORMAT. + - **PostgreSQL** + - If the data type format (datetime format) is omitted from TO_DATE or TO_CHAR, the values are converted in accordance with oracle.nls_date_format.
Statements such as SET can be used to change oracle.nls_date_format. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keywords TO_DATE and TO_CHAR, and check where the data type format (datetime format) is omitted. + 2. Check the settings of the NLS_DATE_FORMAT parameter. + 3. In oracle.nls_date_format, specify the datetime format specified in the NLS_DATE_FORMAT parameter. + +**Migration example** + +The example below shows migration when the date format is specified in the ALTER SESSION statement. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 ALTER SESSION 
+  SET NLS_DATE_FORMAT = "yyyy/mm/dd hh24:mi:ss"; 
+ SELECT o_code, TO_CHAR( SYSDATE ) 
+  FROM ordering_table; 
+  
+
+
 SET orafce.nls_date_format =  
+     'yyyy/mm/dd hh24:mi:ss'; 
+ SELECT o_code, 
+        TO_CHAR( SYSDATE() ) 
+  FROM ordering_table; 
+ 
+
+ +**See** + +---- + +The scope of supported datetime formats differs between Oracle databases and PostgreSQL. Refer to "Formats" for information on the differences in the supported datetime formats. + +---- + +##### 6.2.8.3 Setting a Data Type Format Locale (Setting the Third Argument) + +**Functional differences** + + - **Oracle database** + - The third argument (data type format locale setting) can be specified. + - **PostgreSQL** + - The third argument (data type format locale setting) cannot be specified. + +**Migration procedure** + +The locale cannot be specified in the data type format, so change the server parameters so that the same result is returned. Use the following procedure to perform migration: + + 1. Search for the keywords TO_CHAR and TO_DATE, and identify where they are used. + 2. If the third argument is specified, use a SET statement to specify the corresponding server parameter to match the string format locale to be converted. The table below shows the correspondence between the parameters for setting a data type format locale and the server parameters. + 3. Delete the third argument specified in TO_CHAR and TO_DATE. + +**Correspondence between the parameters for setting a data type format locale and the server parameters** + +|Data type format|Parameter for setting data type format locale
(Oracle database)|Server parameter
(PostgreSQL)| +|:---|:---|:---| +|Number format|NLS_NUMERIC_CHARACTERS|LC_NUMERIC (\*1)| +|Number format|NLS_CURRENCY|LC_MONETARY (\*1)| +|Number format|NLS_ISO_CURRENCY|- (Cannot be migrated because there is no corresponding parameter)| +|Datetime format|NLS_DATE_LANGUAGE|LC_TIME (\*2)(\*3)(\*4)| + +\*1: In Oracle databases, the corresponding string is specified directly, but in PostgreSQL, the locale is specified. The string that is set is the value predetermined for each locale. + +\*2: When a string that is dependent on the specified locale is to be found, the prefix TM must be added at the beginning of the date format. If the TM prefix is not specified, an English-language string will be returned. + +\*3: When a string that is dependent on a Japanese-language or other character set is to be found, the string including the encoding must be specified. (Example: SET LC_TIME='ja_JP.UTF-8') + +\*4: Migration is possible only if TO_CHAR is used to find a string from a date. If TO_DATE is used, a locale-dependent string cannot be used as input. + +**Migration example** + +The example below shows migration when the data type format locale is set (in the third argument). + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 SELECT o_code, 
+        TO_CHAR( o_price * o_quantity / 1.2, 
+                 'l999g999g999d00', 
+                 'NLS_NUMERIC_CHARACTERS = '',.'' 
+                 NLS_CURRENCY = ''EUR'' ' ) "MONEY" 
+   FROM ordering_table;
+
+
 SET LC_MONETARY='de_DE'; 
+ SET LC_NUMERIC='de_DE'; 
+ SELECT o_code, 
+        TO_CHAR( o_price * o_quantity / 1.2, 
+                 'l999g999g999d00' ) "MONEY" 
+  FROM ordering_table;
+
+ + + +**Information** + +---- + +If the data type format matches the client locale, simply delete the third argument of TO_CHAR. + +---- + +**See** + +---- + +The values that can be specified in the server parameters depend on the locale of the operating system on the client. Refer to the PostgreSQL Documentation for details. + +---- + +#### 6.2.9 Functions Requiring Parentheses + +Some functions added by orafce do not have arguments. Parentheses must be added to these functions when they are called. The functions to which parentheses must be added are listed below. +Functions requiring parentheses: + + - SYSDATE + - SESSIONTIMEZONE + - DBTIMEZONE + +**Migration example** + +The example below shows migration when a function that has no arguments is called. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 SELECT SYSDATE FROM DUAL;
+
+
 SELECT SYSDATE() FROM DUAL;
+
+ +### 6.3 Standard Packages + +This section explains how to migrate the standard packages added by orafce. + +#### 6.3.1 DBMS_ALERT + +**Description** + +The DBMS_ALERT package sends alerts from a PL/pgSQL execution session to multiple other PL/pgSQL execution sessions. + +##### 6.3.1.1 Set Value of DBMS_ALERT.REGISTER + +**Functional differences** + + - **Oracle database** + - The second argument of DBMS_ALERT.REGISTER can be specified. The second argument specifies whether to perform a cleanup of the pipe to be used.
The default is TRUE, which causes a cleanup to be performed. + - **PostgreSQL** + - The second argument cannot be specified. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword DBMS_ALERT.REGISTER and identify where it is used. + 2. If the second argument is specified, delete it. + +**Migration example** + +The example below shows migration when the second argument is specified in DBMS_ALERT.REGISTER. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 DBMS_ALERT.REGISTER( 'SAMPLEALERT', TRUE );
+
+
 PERFORM DBMS_ALERT.REGISTER( 'SAMPLEALERT' );
+
+ +##### 6.3.1.2 Case Sensitivity of Alert Names + +**Functional differences** + + - **Oracle database** + - Alert names are case-insensitive. + - **PostgreSQL** + - Alert names are case-sensitive. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keywords DBMS_ALERT.REGISTER, DBMS_ALERT.SIGNAL, DBMS_ALERT.WAITONE, and DBMS_ALERT.REMOVE, and identify where they are used. + 2. If there are alert names in different cases (uppercase and lowercase characters), change them to the same case. + +**Migration example** + +The example below shows migration when there is an alert name in uppercase characters and an alert name in lowercase characters. In this example, the alert names are aligned in uppercase. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 DBMS_ALERT.REGISTER( 'SAMPLEALERT', TRUE ); 
+ ~ 
+ DBMS_ALERT.SIGNAL( 'samplealert', 
+                    'TEST MESSAGE 1' );
+
+
 PERFORM DBMS_ALERT.REGISTER( 'SAMPLEALERT' ); 
+ ~ 
+ PERFORM DBMS_ALERT.SIGNAL( 'SAMPLEALERT', 
+                            'TEST MESSAGE 1' );
+
+ +##### 6.3.1.3 Other Notes on Using DBMS_ALERT + +This section explains the functional differences to be noted when DBMS_ALERT is used. Note that PL/pgSQL features cannot migrate these functional differences. Consider, for example, changing the application logic. + +###### 6.3.1.3.1 Executing DBMS_ALERT.SIGNAL from Multiple PL/pgSQL Sessions + +**Functional differences** + + - **Oracle database** + - DBMS_ALERT.SIGNAL is serialized according to the execution sequence.
Therefore, when DBMS_ALERT.SIGNAL is sent from multiple PL/SQL execution sessions to the same alert,
each DBMS_ALERT.SIGNAL remains in wait state until the preceding DBMS_ALERT.SIGNAL is committed. + - **PostgreSQL** + - DBMS_ALERT.SIGNAL is not serialized according to the execution sequence.
Therefore, even if the preceding DBMS_ALERT.SIGNAL is not yet committed,
the following DBMS_ALERT.SIGNAL does not enter wait state and the alert that is committed first is reported. + +###### 6.3.1.3.2 Message Received when Alert is Reported Multiple Times + +**Functional differences** + + - **Oracle database** + - If multiple DBMS_ALERT.SIGNAL procedures are executed between the time that DBMS_ALERT.REGISTER is executed and DBMS_ALERT.WAITANY/WAITONE is executed, the message from the DBMS_ALERT.SIGNAL executed last is received. All earlier alert messages are discarded. + - **PostgreSQL** + - If multiple DBMS_ALERT.SIGNAL procedures are executed between the time that DBMS_ALERT.REGISTER is executed and DBMS_ALERT.WAITANY/WAITONE is executed, the message from the DBMS_ALERT.SIGNAL executed first is received. Subsequent alert messages are not discarded but retained. + +**Note** + +---- + +If alerts with the same name are used in multiple sessions, ensure that all alert messages are received or delete alerts from the PL/pgSQL sessions by using DBMS_ALERT.REMOVE/REMOVEALL at the point where alerts no longer need to be received. If alerts remain when the session is closed, other sessions may no longer be able to receive alerts properly. + +---- + +##### 6.3.1.4 Example of Migrating DBMS_ALERT + +The example below shows migration to PL/pgSQL when DBMS_ALERT is used. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
(Receiving side) 
+ BEGIN 
+  DBMS_ALERT.REGISTER( 'SAMPLEALERT', TRUE ); 
+ END; 
+ / 
+
+
+ ------------------------------------------------- + (Sending side) +
+ BEGIN + DBMS_ALERT.SIGNAL( 'samplealert', + 'TEST MESSAGE 1' ); + COMMIT; + DBMS_ALERT.SIGNAL( 'samplealert', + 'TEST MESSAGE 2' ); + COMMIT; + END; + / + ------------------------------------------------- + (Receiving side) + SET SERVEROUTPUT ON + DECLARE + alname VARCHAR2(100) := 'SAMPLEALERT'; + almess VARCHAR2(1000); + alst NUMBER; + BEGIN + DBMS_ALERT.WAITONE( alname, almess, alst, 60 ); + DBMS_OUTPUT.PUT_LINE( alname ); + DBMS_OUTPUT.PUT_LINE( almess ); + DBMS_OUTPUT.PUT_LINE( 'alst =' || alst ); + DBMS_ALERT.REMOVE( alname ); + END; + / +
+
+
+
+
 (Receiving side) 
+ DO $$ 
+ BEGIN 
+  PERFORM DBMS_ALERT.REGISTER( 'SAMPLEALERT' ); 
+ END; 
+ $$ 
+ ; 
+ ------------------------------------------------- 
+ (Sending side) 
+ DO $$ 
+ BEGIN 
+  PERFORM DBMS_ALERT.SIGNAL( 'SAMPLEALERT', 
+                             'TEST MESSAGE 1' ); 
+  PERFORM DBMS_ALERT.SIGNAL( 'SAMPLEALERT', 
+                             'TEST MESSAGE 2' ); 
+ END; 
+ $$ 
+ ; 
+
+ ------------------------------------------------- + (Receiving side) + DO $$ + DECLARE + alname VARCHAR2(100) := 'SAMPLEALERT'; + almess VARCHAR2(1000); + alst int; + BEGIN + PERFORM DBMS_OUTPUT.SERVEROUTPUT( TRUE ); + SELECT message, status INTO almess, alst + FROM DBMS_ALERT.WAITONE( alname, 60 ); + PERFORM DBMS_OUTPUT.PUT_LINE( alname ); + PERFORM DBMS_OUTPUT.PUT_LINE( almess ); + PERFORM DBMS_OUTPUT.PUT_LINE( 'alst =' || alst ); + PERFORM DBMS_ALERT.REMOVE( alname ); + END; + $$ + ; +
+
+ + + +#### 6.3.2 DBMS_ASSERT + +**Description** + +The DBMS_ASSERT package checks and normalizes SQL syntax elements. + +##### 6.3.2.1 DBMS_ASSERT.ENQUOTE_LITERAL + +**Functional differences** + + - **Oracle database** + - If a string in an argument is already enclosed in single quotation marks, it is not again enclosed in single quotation marks. + - **PostgreSQL** + - Even if a string in an argument is already enclosed in single quotation marks, it is again enclosed in single quotation marks. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword DBMS_ASSERT.ENQUOTE_LITERAL and identify where it is used. + 2. In the conditions of an IF statement, use LEFT and RIGHT to check the leading and trailing characters. + 3. If each result does not match a single quotation mark (E'\x27'), use ENQUOTE_LITERAL to replace it. + +**Migration example** + +The example below shows migration when a string is enclosed in single quotation marks. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 DBMS_OUTPUT.PUT_LINE( 
+  DBMS_ASSERT.ENQUOTE_LITERAL( en_lit ) ); 
+
+
+
+
+
+
+
 IF ( LEFT( en_lit, 1 ) = E'\x27' AND 
+      RIGHT( en_lit, 1 ) = E'\x27' ) THEN 
+   PERFORM DBMS_OUTPUT.PUT_LINE( en_lit ); 
+ ELSE 
+  PERFORM DBMS_OUTPUT.PUT_LINE( 
+   DBMS_ASSERT.ENQUOTE_LITERAL( en_lit ) ); 
+ END IF;
+
+ +**Note** + +---- + +PostgreSQL does not verify single quotation marks. + +---- + +##### 6.3.2.2 DBMS_ASSERT.ENQUOTE_NAME + +**Functional differences** + + - **Oracle database** + - If the string in the first argument is already enclosed in double quotation marks, it is not again enclosed in double quotation marks.
In addition, regardless of whether there is a second argument, a string enclosed in double quotation marks is not converted from lowercase to uppercase. + - **PostgreSQL** + - Even if the string in the first argument is already enclosed in double quotation marks, it is again enclosed in double quotation marks.
However, a first argument string that is all in lowercase is not enclosed in double quotation marks.
In addition, if the second argument is set to TRUE or the default, it is converted from uppercase to lowercase even if it is enclosed in double quotation marks. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword DBMS_ASSERT.ENQUOTE_NAME and identify where it is used. + 2. In the conditions of an IF statement, use LEFT and RIGHT to check the leading and trailing characters. + 3. If each result does not match a double quotation mark (E'\x27'), use ENQUOTE_NAME to replace it. + +**Migration example** + +The example below shows migration when a string is enclosed in double quotation marks. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 DBMS_OUTPUT.PUT_LINE( 
+  DBMS_ASSERT.ENQUOTE_NAME( en_nam ) ); 
+
+
+
+
+
+
+
 IF ( LEFT( en_nam, 1 ) = E'\x22' AND 
+      RIGHT( en_nam, 1 ) = E'\x22' ) THEN 
+   PERFORM DBMS_OUTPUT.PUT_LINE( en_nam ); 
+ ELSE 
+  PERFORM DBMS_OUTPUT.PUT_LINE( 
+   DBMS_ASSERT.ENQUOTE_NAME( en_nam ) ); 
+ END IF;
+
+ +##### 6.3.2.3 DBMS_ASSERT.SIMPLE_SQL_NAME + +**Functional differences** + + - **Oracle database** + - If the leading or trailing position of a string in an argument contains a space, the space is deleted before the string is evaluated. + - **PostgreSQL** + - If the leading or trailing position of a string in an argument contains a space, the string is evaluated as is, causing an error. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword DBMS_ASSERT.SIMPLE_SQL_NAME and identify where it is used. + 2. If the leading or trailing position of a string in an argument contains a space, use TRIM to delete the space immediately preceding or following the argument string. + +**Migration example** + +The example below shows migration when the leading or trailing position of a string in an argument contains a space. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
DBMS_OUTPUT.PUT_LINE( 
+  DBMS_ASSERT.SIMPLE_SQL_NAME( si_nam ) ); 
+  
+
+
 PERFORM DBMS_OUTPUT.PUT_LINE( 
+  DBMS_ASSERT.SIMPLE_SQL_NAME( 
+   TRIM( both from si_nam ) ) );
+
+ +**See** + +---- + +The strings checked by DBMS_ASSERT.SIMPLE_SQL_NAME correspond to identifiers among the SQL elements. Refer to "The SQL Language" > "Lexical Structure" > "Identifiers and Key Words" in the PostgreSQL Documentation for information on the values that can be used as identifiers in PostgreSQL. + +---- + +##### 6.3.2.4 DBMS_ASSERT.SQL_OBJECT_NAME +**Functional differences** + + - **Oracle database** + - DBMS_ASSERT.SQL_OBJECT_NAME exists. + - **PostgreSQL** + - DBMS_ASSERT.SQL_OBJECT_NAME does not exist. Use DBMS_ASSERT.OBJECT_NAME instead. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword DBMS_ASSERT.SQL_OBJECT_NAME and identify where it is used. + 2. Change DBMS_ASSERT.SQL_OBJECT_NAME to DBMS_ASSERT.OBJECT_NAME. + +**Migration example** + +The example below shows migration when an input value is verified as a qualified SQL identifier of an existing SQL object. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 SELECT 
+   DBMS_ASSERT.SQL_OBJECT_NAME( 'inventory_table' ) 
+  INTO table_name 
+  FROM DUAL;
+
+
 SELECT 
+   DBMS_ASSERT.OBJECT_NAME( 'inventory_table' ) 
+  INTO table_name 
+  FROM DUAL;
+
+ + +##### 6.3.2.5 Example of Migrating DBMS_ASSERT +The example below shows migration to PL/pgSQL when DBMS_ASSERT is used. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 SET SERVEROUTPUT ON 
+ DECLARE 
+  en_lit VARCHAR2(50) := '''ENQUOTE_LITERAL'''; 
+  en_nam VARCHAR2(50) := '"enquote_name"'; 
+  si_nam VARCHAR2(50) := ' SIMPLE_SQL_NAME   '; 
+  table_name VARCHAR2(20); 
+ BEGIN 
+
+ DBMS_OUTPUT.PUT_LINE( + DBMS_ASSERT.ENQUOTE_LITERAL( en_lit )); +
+
+
+
+
+
+ DBMS_OUTPUT.PUT_LINE( + DBMS_ASSERT.ENQUOTE_NAME( en_nam )); +
+
+
+
+
+
+ DBMS_OUTPUT.PUT_LINE( + DBMS_ASSERT.SIMPLE_SQL_NAME( si_nam )); +
+
+ SELECT DBMS_ASSERT.SQL_OBJECT_NAME( + 'inventory_table' ) + INTO table_name + FROM DUAL; + DBMS_OUTPUT.PUT_LINE( + 'Object is : ' || table_name ); + END; + / +
+
+
 DO $$ 
+ DECLARE 
+  en_lit VARCHAR2(50) := '''ENQUOTE_LITERAL''';  
+  en_nam VARCHAR2(50) := '"enquote_name"';  
+  si_nam VARCHAR2(50) := ' SIMPLE_SQL_NAME   ';  
+  table_name VARCHAR2(20);  
+ BEGIN 
+  PERFORM DBMS_OUTPUT.SERVEROUTPUT(TRUE);  
+  IF ( LEFT( en_lit, 1 ) = E'\x27' AND 
+       RIGHT( en_lit, 1 ) = E'\x27' ) THEN 
+   PERFORM DBMS_OUTPUT.PUT_LINE( en_lit );  
+  ELSE 
+   PERFORM DBMS_OUTPUT.PUT_LINE( 
+    DBMS_ASSERT.ENQUOTE_LITERAL( en_lit )); 
+  END IF; 
+
+ IF ( LEFT( en_nam, 1 ) = E'\x22' AND + RIGHT( en_nam, 1 ) = E'\x22' ) THEN + PERFORM DBMS_OUTPUT.PUT_LINE( en_nam ); + ELSE + PERFORM DBMS_OUTPUT.PUT_LINE( + DBMS_ASSERT.ENQUOTE_NAME( en_nam ) ); + END IF; +
+ PERFORM DBMS_OUTPUT.PUT_LINE( + DBMS_ASSERT.SIMPLE_SQL_NAME( + TRIM( both from si_nam ) ) ); +
+ SELECT DBMS_ASSERT.OBJECT_NAME( + 'inventory_table' ) + INTO table_name + FROM DUAL; + PERFORM DBMS_OUTPUT.PUT_LINE( + 'Object is : ' || table_name ); + END; + $$ + ;
+
+ +#### 6.3.3 DBMS_OUTPUT +**Description** + +The DBMS_OUTPUT package sends messages from PL/pgSQL to clients such as psql. + +##### 6.3.3.1 Differences in the Timing of Output Immediately After DBMS_OUTPUT.SERVEROUTPUT Changes from OFF to ON + +**Functional differences** + + - **Oracle database** + - Messages stored in the buffer while SERVEROUTPUT is OFF are displayed after the execution of the first SQL statement or anonymous PL/SQL after SERVEROUTPUT changes to ON. + - **PostgreSQL** + - Messages stored in the buffer while SERVEROUTPUT is FALSE are not displayed even after the execution of the first SQL statement or anonymous block after SERVEROUTPUT changes to TRUE. DBMS_OUT.NEW_LINE must be executed. + +**Migration procedure** + +Use the following procedure to perform migration: + 1. Search for the keyword SERVEROUTPUT and identify where it changes from OFF to ON. + 2. Change the code so that DBMS_OUT.NEW_LINE is executed immediately after the SQL statement or anonymous block that is executed after the SERVEROUTPUT statement is changed to ON. + +**Migration example** + +The example below shows migration when the status of SERVEROUTPUT changes. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 SET SERVEROUTPUT OFF; 
+
+
+
+
+
+ ... +
+ SET SERVEROUTPUT ON; + SELECT * FROM dual; +
+
+
+
+
+
+
+
 DO $$ 
+ BEGIN 
+ PERFORM DBMS_OUTPUT.SERVEROUTPUT( FALSE ); 
+ END; 
+ $$ 
+ ; 
+ ... 
+
+ SELECT * FROM dual; + DO $$ + BEGIN + PERFORM DBMS_OUTPUT.SERVEROUTPUT( TRUE ); + PERFORM DBMS_OUTPUT.NEW_LINE(); + END; + $$ + ; +
+
+ +##### 6.3.3.2 Other Notes on Using DBMS_OUTPUT + +This section explains the functional differences to be noted when DBMS_OUTPUT is used. Note that PL/pgSQL features cannot migrate these functional differences. Consider, for example, changing the application logic. + +###### 6.3.3.2.1 Differences in the Output Timing of DBMS_OUTPUT.PUT_LINE and DBMS_OUTPUT.NEW_LINE + +**Functional differences** + + - **Oracle database** + - When SERVEROUTPUT is ON, the outputs of DBMS_OUTPUT.PUT_LINE and DBMS_OUTPUT.NEW_LINE are displayed together after the procedure finishes.
These outputs are stored in the buffer of the server while the procedure is running. + - **PostgreSQL** + - When SERVEROUTPUT is TRUE, the outputs from executing DBMS_OUTPUT.PUT_LINE and DBMS_OUTPUT.NEW_LINE are sent to the client and displayed immediately.
They are not stored in the buffer of the server. + +##### 6.3.3.3 Example of Migrating DBMS_OUTPUT +The example below shows migration to PL/pgSQL when DBMS_OUTPUT is used. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 SET SERVEROUTPUT OFF; 
+ BEGIN 
+
+ DBMS_OUTPUT.ENABLE( NULL ); + DBMS_OUTPUT.PUT_LINE( '1:Hello World' ); + END; + / +
+ SET SERVEROUTPUT ON + SELECT * FROM dual; +
+
+
+
+
+
+
+
+
 DO $$ 
+ BEGIN 
+  PERFORM DBMS_OUTPUT.SERVEROUTPUT( FALSE ); 
+  PERFORM DBMS_OUTPUT.ENABLE( NULL ); 
+  PERFORM DBMS_OUTPUT.PUT_LINE( '1:Hello World' ); 
+ END; 
+ $$ 
+ ; 
+ SELECT * FROM dual; 
+ DO $$ 
+  BEGIN 
+   PERFORM DBMS_OUTPUT.SERVEROUTPUT( TRUE ); 
+   PERFORM DBMS_OUTPUT.NEW_LINE(); 
+ END; 
+ $$ 
+ ; 
+
+ +#### 6.3.4 DBMS_PIPE +**Description** + +The DBMS_PIPE package performs one-to-one communication between PL/pgSQL sessions. + +##### 6.3.4.1 Differences from the DBMS_PIPE.CREATE_PIPE Definition +**Functional differences** + + - **Oracle database** + - The second argument specifies the maximum size of the pipe in bytes. The default is 8192 bytes.
The third argument specifies the pipe type. The default is TRUE (private pipe). + - **PostgreSQL** + - The second argument specifies the maximum number of messages that the pipe can hold. The default is 0. The specifiable range of numeric values is 1 to 32767.
The third argument specifies the pipe type. The default is FALSE (public pipe). + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword DBMS_PIPE.CREATE_PIPE and identify where it is used. + 2. Change the code so that the maximum number of messages is specified in the second argument. + 3. If the third argument is omitted and a private pipe is to be created, specify TRUE in the third argument. + +**Note** + +---- + +Preferably, create a public pipe (the default) as the pipe type. If you create a private pipe, internal information (the creator of the private pipe) will remain even after the pipe is removed. Thus repeatedly creating and removing pipes may ultimately cause memory to run out. + +---- + +**Migration example** + +The example below shows migration of DBMS_PIPE.CREATE_PIPE. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 DBMS_PIPE.CREATE_PIPE( 
+           'pipename', 
+           2000,  
+           TRUE );
+
+
 DBMS_PIPE.CREATE_PIPE( 
+           'pipename', 
+           50, 
+           TRUE );
+
+ +##### 6.3.4.2 Return Values of DBMS_PIPE.CREATE_PIPE and DBMS_PIPE.REMOVE_PIPE +**Functional differences** + + - **Oracle database** + - DBMS_PIPE.CREATE_PIPE and DBMS_PIPE.REMOVE_PIPE both return values. + - **PostgreSQL** + - DBMS_PIPE.CREATE_PIPE and DBMS_PIPE.REMOVE_PIPE both do not return values. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keywords DBMS_PIPE.CREATE_PIPE and DBMS_PIPE.REMOVE_PIPE, and identify where they are used. + 2. Change the code so that the call processing identified in step 1 is called by the PERFORM keyword. + 3. If return values are used, replace the target processing with 0. + +**Migration example** + +The example below shows migration of DBMS_PIPE.CREATE_PIPE. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 st := DBMS_PIPE.CREATE_PIPE( pipename, 2000 ); 
+ DBMS_OUTPUT.PUT_LINE( 'Return Value =' || st ); 
+
+
+
+
 PERFORM DBMS_PIPE.CREATE_PIPE( pipename, 50 ); 
+ st := 0; 
+ PERFORM DBMS_OUTPUT.PUT_LINE( 
+                  'Return Value =' || st );
+
+ +##### 6.3.4.3 Creating the Same Pipe Name with DBMS_PIPE.CREATE_PIPE +**Functional differences** + + - **Oracle database** + - If a pipe with the same name already exists and can be used, DBMS_PIPE.CREATE_PIPE returns normally. + - **PostgreSQL** + - If a pipe with the same name already exists, DBMS_PIPE.CREATE_PIPE returns with an error. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword CREATE_PIPE and identify where it is used. + 2. If there may be a pipe with the same name, use the PERFORM statement shown below to check if the same pipe exists. + 3. If NOT FOUND returns TRUE, there is no pipe with the same name, so execute CREATE_PIPE. + +~~~ +PERFORM 1 + FROM DBMS_PIPE.DB_PIPES + WHERE NAME = nameOfPipeToBeCreated +~~~ + +**Migration example** + +The example below shows migration of CREATE_PIPE when there may be a pipe with the same name. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
  
+ DECLARE 
+  pipename VARCHAR2(1000) := 'TESTPIPE01'; 
+ BEGIN 
+  DBMS_OUTPUT.PUT_LINE( 
+   'Return = '|| DBMS_PIPE.CREATE_PIPE( 
+                           pipename, 
+                           2000, 
+                           TRUE ) ); 
+
+
+
+ END; + / +
+
+
 DO $$ 
+ DECLARE 
+ pipename VARCHAR2(1000) := 'TESTPIPE01'; 
+ BEGIN 
+  PERFORM 1 
+   FROM DBMS_PIPE.DB_PIPES 
+   WHERE NAME = pipename;  
+  IF ( NOT FOUND ) THEN 
+   PERFORM DBMS_PIPE.CREATE_PIPE( pipename, 
+                                  50, 
+                                  TRUE ); 
+ END IF; 
+ END; 
+ $$ 
+ ;
+
+ +##### 6.3.4.4 Return Values of DBMS_PIPE.NEXT_ITEM_TYPE +**Functional differences** + + - **Oracle database** + - DBMS_PIPE.NEXT_ITEM_TYPE has the following return values:
0: There is no next item.
6: NUMBER type
9: VARCHAR2 type
11: ROWID type
12: DATE type
23: RAW type + - **PostgreSQL** + - DBMS_PIPE.NEXT_ITEM_TYPE has the following return values:
0: There is no next item.
9: NUMERIC type
11: TEXT type
12: DATE type
13: TIMESTAMP type
23: BYTEA type
24: RECORD type + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword NEXT_ITEM_TYPE and identify the variable storing the return value of NEXT_ITEM_TYPE. + 2. If the return value of NEXT_ITEM_TYPE is determined, change it to the value in PostgreSQL according to the table below. + +**Correspondence of return values of DBMS_PIPE.NEXT_ITEM_TYPE** + +|Oracle database|PostgreSQL| +|:---|:---| +| NUMBER type | NUMERIC type | +| VARCHAR2 type | TEXT type | +| ROWID type | | +| | DATE type | +| DATE type | TIMESTAMP type | +| RAW type | BYTEA type | +| | RECORD type | + +**Migration example** + +The example below shows migration when processing is branched according to the return value of DBMS_PIPE.NEXT_ITEM_TYPE. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 item := DBMS_PIPE.NEXT_ITEM_TYPE; 
+ IF ( item = 6 ) THEN  -- NUMBER type 
+ ~ 
+ ELSIF ( item = 9 ) THEN -- VARCHAR2 type 
+ ~ 
+ ELSIF ( item = 12 ) THEN  -- DATE type 
+ ~
+
+
 item := DBMS_PIPE.NEXT_ITEM_TYPE(); 
+ IF ( item = 9 ) THEN  -- NUMERIC type 
+ ~ 
+ ELSIF ( item =11 ) THEN -- TEXT type 
+ ~ 
+ ELSIF ( item = 13 ) THEN -- TIMESTAMP type 
+ ~
+
+ + +##### 6.3.4.5 Data Types That Can be Used in DBMS_PIPE.PACK_MESSAGE and UNPACK_MESSAGE +**Functional differences** + + - **Oracle database** + - The data types that can be used are VARCHAR2, NCHAR, NUMBER, DATE, RAW, and ROWID.
When RAW or ROWID is used, the data type must be specified after UNPACK_MESSAGE. + - **PostgreSQL** + - The data types that can be used are TEXT, NUMERIC, INTEGER (Note), BIGINT (Note), DATE, TIMESTAMP, BYTEA, and RECORD.
All data types require the data type and empty parentheses to be specified after UNPACK_MESSAGE. + +**Note** + +---- + + - The INTEGER and BIGINT data types can be used with PACK_MESSAGE only. + - The INTEGER and BIGINT types are converted internally to the NUMERIC type. Therefore, use UNPACK_MESSAGE_NUMBER to receive a message. + +---- + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword UNPACK_MESSAGE and identify where UNPACK_MESSAGE is used. + 2. Change the variable specified in the argument to an assignment expression specified on the left-hand side, separately specify each data type after UNPACK_MESSAGE, and delete the variable from the parentheses. + +**Migration example** + +The example below shows migration when a message is sent and received. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
DBMS_PIPE.UNPACK_MESSAGE( testnum );
+
+
 testnum := 
+     DBMS_PIPE.UNPACK_MESSAGE_NUMBER();
+
+ +##### 6.3.4.6 Case Sensitivity of DBMS_PIPE.RECEIVE_MESSAGE and SEND_MESSAGE +**Functional differences** + + - **Oracle database** + - Pipe names are case-insensitive. + - **PostgreSQL** + - Pipe names are case-sensitive. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keywords RECEIVE_MESSAGE and SEND_MESSAGE, and check the pipe names. + 2. If there are pipe names in different cases (uppercase and lowercase characters), change them to the same case. + +**Migration example** + +The example below shows migration when uppercase and lowercase characters are used for the pipe names. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 (Sending side) 
+ st := DBMS_PIPE.SEND_MESSAGE( 'TESTPIPE01', 
+                               10, 
+                               8192 ); 
+ (Receiving side) 
+ st := DBMS_PIPE.RECEIVE_MESSAGE( 'testpipe01' );
+
+
 (Sending side) 
+ st := DBMS_PIPE.SEND_MESSAGE( 'TESTPIPE01', 
+                               10, 
+                               100 );  
+ (Receiving side) 
+ st := DBMS_PIPE.RECEIVE_MESSAGE( 'TESTPIPE01' );
+
+ + +**Note** + +---- + +The return values of DBMS_PIPE.RECEIVE_MESSAGE and DBMS_PIPE.SEND_MESSAGE differ as shown below. + + - **Oracle database** + - There are five return values, as follows:
0: Completed successfully.
1: A timeout occurred.
2: A record in the pipe is too big for the buffer.
3: An interrupt occurred.
ORA-23322: The user does not have privileges for reading the pipe. + - **PostgreSQL** + - There are two return values, as follows:
0: Completed successfully.
1: A timeout occurred. + +---- + +##### 6.3.4.7 Differences in the DBMS_PIPE.SEND_MESSAGE Feature + +**Functional differences** + + - **Oracle database** + - The third argument specifies the maximum size of the pipe in bytes. The default is 8192 bytes. + - **PostgreSQL** + - The third argument specifies the maximum number of messages that the pipe can hold.
The specifiable range of numeric values is 1 to 32767.
Note that if the maximum number of messages is omitted for an implicit pipe, the number is unlimited. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keyword SEND_MESSAGE and identify where the maximum number of bytes is specified. + 2. Replace the maximum number of bytes with the maximum number of messages. + +**Migration example** + +The example below shows migration when the maximum pipe size is specified. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 DBMS_PIPE.SEND_MESSAGE( 'testPIPE', 10, 200 );
+
+
 DBMS_PIPE.SEND_MESSAGE( 'testPIPE', 10, 10 );
+
+ +### 6.3.4.8 Example of Migrating DBMS_PIPE +The example below shows migration when one-to-one communication is performed between PL/pgSQL sessions. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 (Sending side) 
+ SET SERVEROUTPUT ON; 
+ DECLARE 
+  testnum NUMBER := 111; 
+  testvchar2 VARCHAR2(100) := 'Test Message'; 
+  testdate DATE := SYSDATE; 
+  testraw RAW(100) := '0101010101'; 
+  st INT; 
+  pipename VARCHAR2(1000) := 'TESTPIPE01'; 
+ BEGIN 
+
+ st := DBMS_PIPE.CREATE_PIPE( pipename, 2000 ); +
+
+
+
+
+ DBMS_OUTPUT.PUT_LINE( 'Return Value =' || st ); +
+
+ DBMS_PIPE.PACK_MESSAGE( testnum ); + DBMS_PIPE.PACK_MESSAGE( testvchar2 ); + DBMS_PIPE.PACK_MESSAGE( testdate ); + DBMS_PIPE.PACK_MESSAGE_RAW( testraw ); + st := DBMS_PIPE.SEND_MESSAGE( 'TESTPIPE01', + 10, + 200 ); + DBMS_OUTPUT.PUT_LINE( 'Return Value =' || st ); +
+ END; + / +
+ ------------------------------------------------- + (Receiving side) + SET SERVEROUTPUT ON; + DECLARE + testnum NUMBER; + testvchar2 VARCHAR2(100); + testdate DATE; + testraw RAW(100); + item NUMBER; + st INT; + BEGIN +
+ st := DBMS_PIPE.RECEIVE_MESSAGE( 'testpipe01' ); + DBMS_OUTPUT.PUT_LINE( 'Return Value =' || st ); +
+ LOOP + item := DBMS_PIPE.NEXT_ITEM_TYPE; + DBMS_OUTPUT.PUT_LINE( 'Next Item : ' || item ); +
+ IF ( item = 6 ) THEN + DBMS_PIPE.UNPACK_MESSAGE( testnum ); +
+ DBMS_OUTPUT.PUT_LINE( + 'Get Message : ' || testnum ); + ELSIF ( item = 9 ) THEN + DBMS_PIPE.UNPACK_MESSAGE( testvchar2 ); +
+ DBMS_OUTPUT.PUT_LINE( + 'Get Message : ' || testvchar2 ); + ELSIF ( item = 12 ) THEN + DBMS_PIPE.UNPACK_MESSAGE( testdate ); +
+ DBMS_OUTPUT.PUT_LINE( + 'Get Message : ' || testdate ); + ELSIF ( item = 23 ) THEN + DBMS_PIPE.UNPACK_MESSAGE_RAW( testraw ); +
+
+
+ DBMS_OUTPUT.PUT_LINE( + 'Get Message : ' || testraw ); + ELSE + EXIT; + END IF; + END LOOP; + st := DBMS_PIPE.REMOVE_PIPE( 'testpipe01' ); +
+ DBMS_OUTPUT.PUT_LINE( 'Return Value =' || st ); +
+ END; + / +
+
+
 (Sending side) 
+ DO $$ 
+ DECLARE 
+  testnum NUMERIC := 111; 
+  testtext VARCHAR2(100) := 'Test Message'; 
+  testtime TIMESTAMP := current_timestamp; 
+  testbytea BYTEA := '0101010101'; 
+  st INT; 
+  pipename VARCHAR2(1000) := 'TESTPIPE01'; 
+ BEGIN 
+  PERFORM DBMS_OUTPUT.SERVEROUTPUT( TRUE ); 
+  PERFORM 1 
+   FROM DBMS_PIPE.DB_PIPES 
+    WHERE NAME = pipename;  
+  IF ( NOT FOUND ) THEN 
+   PERFORM DBMS_PIPE.CREATE_PIPE( pipename,50 ); 
+   st := 0; 
+   PERFORM DBMS_OUTPUT.PUT_LINE( 
+                    'Return Value =' || st ); 
+  END IF; 
+  PERFORM DBMS_PIPE.PACK_MESSAGE( testnum ); 
+  PERFORM DBMS_PIPE.PACK_MESSAGE( testtext ); 
+  PERFORM DBMS_PIPE.PACK_MESSAGE( testtime ); 
+  PERFORM DBMS_PIPE.PACK_MESSAGE( testbytea ); 
+  st := DBMS_PIPE.SEND_MESSAGE( 'TESTPIPE01', 
+                                10, 
+                                10 );  
+  PERFORM DBMS_OUTPUT.PUT_LINE( 
+  'Return Value =' || st ); 
+ END; 
+ $$ 
+ ; 
+ ------------------------------------------------- 
+ (Receiving side) 
+ DO $$ 
+ DECLARE 
+  testnum NUMERIC; 
+  testtext VARCHAR2(100); 
+  testtime TIMESTAMP; 
+  testbytea BYTEA; 
+  item INT; 
+  st INT; 
+ BEGIN 
+  PERFORM DBMS_OUTPUT.SERVEROUTPUT( TRUE ); 
+  st := DBMS_PIPE.RECEIVE_MESSAGE( 'TESTPIPE01' ); 
+  PERFORM DBMS_OUTPUT.PUT_LINE( 
+                      'Return Value ='|| st ); 
+  LOOP 
+   item := DBMS_PIPE.NEXT_ITEM_TYPE(); 
+   PERFORM DBMS_OUTPUT.PUT_LINE( 
+                       'Next Item : ' || item ); 
+   IF ( item = 9 ) THEN 
+    testnum := 
+     DBMS_PIPE.UNPACK_MESSAGE_NUMBER(); 
+    PERFORM DBMS_OUTPUT.PUT_LINE( 
+                        'Get Message : ' || testnum ); 
+   ELSIF ( item =11 ) THEN 
+    testtext := 
+     DBMS_PIPE.UNPACK_MESSAGE_TEXT(); 
+    PERFORM DBMS_OUTPUT.PUT_LINE( 
+                        'Get Message : ' || testtext ); 
+   ELSIF ( item = 13 ) THEN 
+    testtime := 
+     DBMS_PIPE.UNPACK_MESSAGE_TIMESTAMP(); 
+    PERFORM DBMS_OUTPUT.PUT_LINE( 
+                        'Get Message : ' || testtime ); 
+   ELSIF ( item = 23 ) THEN 
+    testbytea := 
+     DBMS_PIPE.UNPACK_MESSAGE_BYTEA(); 
+    testtext := CAST( testbytea 
+     AS varchar2(100) ); 
+        PERFORM DBMS_OUTPUT.PUT_LINE( 
+                            'Get Message : ' || testtext ); 
+   ELSE 
+    EXIT; 
+   END IF; 
+  END LOOP; 
+  PERFORM DBMS_PIPE.REMOVE_PIPE( 'TESTPIPE01' ); 
+  st := 0; 
+  PERFORM DBMS_OUTPUT.PUT_LINE( 
+                      'Return Value ='|| st ); 
+ END; 
+ $$ 
+ ; 
+ 
+
+ +#### 6.3.5 UTL_FILE + +**Description** + +The UTL_FILE package enables PL/pgSQL to read and write text files. + +##### 6.3.5.1 Appending a Newline at File Closure + +**Functional differences** + + - **Oracle database** + - If data in which no newline is specified remains in the buffer, a newline is appended after the data is output and then the file is closed. + - **PostgreSQL** + - If data in which no newline is specified remains in the buffer, the data is output and then the file is closed. A newline is not appended. + +**Migration procedure** + +Use the following procedure to perform migration: + + 1. Search for the keywords UTL_FILE.FCLOSE and UTF_FILE.FCLOSE_ALL, and identify where they are used. + 2. If UTL_FILE.PUT is executed and no newline is specified during write processing before the file is closed, change the code so that UTL_FILE.NEW_LINE is executed before the file is closed. + +**Migration example** + +The example below shows migration when a file that does not end with a newline is closed. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 UTL_FILE.PUT(v_handle, buff); 
+ UTL_FILE.FCLOSE(v_handle); 
+ 
+
+
 PERFORM UTL_FILE.PUT(v_handle, buff); 
+ PERFORM UTL_FILE.NEW_LINE(v_handle, 1); 
+ s_handle := UTL_FILE.FCLOSE(v_handle);
+
+ + +##### 6.3.5.2 Processing UTL_FILE Exceptions + +**Functional differences** + + - **Oracle database** + - There are exception definitions for the UTL_FILE package. They can be used for determining exceptions in the EXCEPTION clause. + - **PostgreSQL** + - There are no exception definitions for the UTL_FILE package. + +**Migration procedure** + +There are no exception definitions for the UTL_FILE package, so if they are used for determining exceptions in the EXCEPTION clause, replace them with PostgreSQL error codes. Use the following procedure to perform migration: + + 1. Search for the keyword UTL_FILE and check if an EXCEPTION clause is specified in the target PL/SQL. + 2. If a UTL_FILE exception is used, replace it with a PostgreSQL error code in accordance with the table below. + +**Correspondence of UTL_FILE exceptions** + +|UTL_FILE exception definition
(Oracle database)|Migratability|Corresponding PostgreSQL error code| +|:---|:---|:---| +|INVALID_PATH|Y|RAISE_EXCEPTION| +|INVALID_MODE|Y|RAISE_EXCEPTION| +|INVALID_FILEHANDLE|Y|RAISE_EXCEPTION| +|INVALID_OPERATION|Y|RAISE_EXCEPTION| +|READ_ERROR|N|Not generated| +|WRITE_ERROR|Y|RAISE_EXCEPTION| +|INTERNAL_ERROR|Y|INTERNAL_ERROR| +|CHARSETMISMATCH|N|Not generated| +|FILE_OPEN|N|Not generated| +|INVALID_MAXLINESIZE|Y|RAISE_EXCEPTION| +|INVALID_FILENAME|Y|INVALID PARAMETER
NULL VALUE NOT ALLOWED (file name is NULL)| +|ACCESS_DENIED|Y|RAISE_EXCEPTION| +|INVALID_OFFSET|N|Not generated| +|DELETE_FAILED|N|Not generated| +|RENAME_FAILED|Y|RAISE_EXCEPTION| + +Y: Can be migrated + +N: Cannot be migrated + +**Migration example** + +The example below shows migration when an error message is displayed during UTL_FILE exception processing. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 EXCEPTION 
+  WHEN UTL_FILE.INVALID_FILEHANDLE THEN 
+   v_errmsg := SQLERRM; 
+   DBMS_OUTPUT.PUT_LINE(v_errmsg); 
+ END;
+
+
 EXCEPTION 
+  WHEN RAISE_EXCEPTION THEN 
+   v_errmsg := SQLERRM;	 
+   PERFORM DBMS_OUTPUT.PUT_LINE(v_errmsg); 
+ END;
+
+ + + +##### 6.3.5.3 Other Notes on Using UTL_FILE +This section explains the functional differences to be noted when UTL_FILE is used. Note that PL/pgSQL features cannot migrate these functional differences. Consider, for example, changing the application logic. + +###### 6.3.5.3.1 Differences in the Open Mode of UTL_FILE.FOPEN + +**Functional differences** + + - **Oracle database** + - The rb (read byte), wb (write byte), or ab (append byte) open mode can be specified. + - **PostgreSQL** + - The rb (read byte), wb (write byte), and ab (append byte) open modes cannot be specified for OPEN_MODE. + +###### 6.3.5.3.2 Differences in UTL_FILE.IS_OPEN + +**Functional differences** + + - **Oracle database** + - Executing UTL_FILE.IS_OPEN after UTL_FILE.FCLOSE_ALL returns TRUE. + - **PostgreSQL** + - Executing UTL_FILE.IS_OPEN after UTL_FILE.FCLOSE_ALL returns FALSE. + +###### 6.3.5.3.3 Timing of Write by UTL_FILE.FFLUSH + +**Functional differences** + + - **Oracle database** + - Buffered data up to the newline character is written. + - **PostgreSQL** + - All buffered data is written. + +##### 6.3.5.4 Example of Migrating UTL_FILE + +The example below shows migration to PL/pgSQL when UTL_FILE is used. + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
 SET SERVEROUTPUT ON 
+ DECLARE 
+  v_handle   UTL_FILE.FILE_TYPE; 
+  v_dirname  VARCHAR2(250); 
+  v_filename VARCHAR2(250); 
+  v_output   VARCHAR2(250); 
+  v_getmsg   VARCHAR2(250); 
+  v_errmsg   VARCHAR2(1000); 
+  v_opcheck  BOOLEAN; 
+ BEGIN 
+  v_dirname := '/home/oracle'; 
+  v_filename := 'sample.txt'; 
+  v_output := 'HELLO WORLD!'; 
+  v_handle := UTL_FILE.FOPEN(v_dirname, 
+                             v_filename, 
+                             'w', 
+                             256); 
+
+
+ UTL_FILE.PUT_LINE(v_handle, v_output); + UTL_FILE.FFLUSH(v_handle); + UTL_FILE.PUT(v_handle, v_output); +
+ UTL_FILE.FCLOSE(v_handle); + v_handle := UTL_FILE.FOPEN(v_dirname, + v_filename, + 'r', + 256); + UTL_FILE.GET_LINE(v_handle, v_getmsg); + DBMS_OUTPUT.PUT_LINE( + 'GET_MESSAGE : ' || v_getmsg); + UTL_FILE.FCLOSE_ALL; + v_opcheck := UTL_FILE.IS_OPEN(v_handle); + DBMS_OUTPUT.PUT_LINE(CASE + WHEN v_opcheck IS NULL THEN 'UNKNOWN' + WHEN v_opcheck THEN 'TRUE' + WHEN NOT v_opcheck THEN 'FALSE' + END); +
+ BEGIN + UTL_FILE.PUT_LINE(v_handle, v_output); + EXCEPTION + WHEN UTL_FILE.INVALID_FILEHANDLE THEN + v_errmsg := SQLERRM; + DBMS_OUTPUT.PUT_LINE(v_errmsg); + END; +
+ EXCEPTION WHEN OTHERS THEN + UTL_FILE.FCLOSE_ALL; + v_errmsg := SQLERRM; + DBMS_OUTPUT.PUT_LINE(v_errmsg); + END; + / +
+
+
 DO $$ 
+ DECLARE 
+  v_handle   UTL_FILE.FILE_TYPE; 
+  v_dirname  VARCHAR2(250); 
+  v_filename VARCHAR2(250); 
+  v_output   VARCHAR2(250); 
+  v_getmsg   VARCHAR2(250); 
+  v_errmsg   VARCHAR2(1000); 
+  v_opcheck  BOOLEAN; 
+ BEGIN 
+  PERFORM DBMS_OUTPUT.SERVEROUTPUT(TRUE); 
+  PERFORM DBMS_OUTPUT.ENABLE(NULL); 
+  v_dirname := '/home/pgsql'; 
+  v_filename := 'sample.txt'; 
+  v_output := 'HELLO WORLD!'; 
+  v_handle := UTL_FILE.FOPEN(v_dirname, 
+                             v_filename, 
+                             'w', 
+                             256); 
+  PERFORM UTL_FILE.PUT_LINE(v_handle, v_output); 
+  PERFORM UTL_FILE.PUT(v_handle, v_output); 
+  PERFORM UTL_FILE.FFLUSH(v_handle); 
+  PERFORM UTL_FILE.NEW_LINE(v_handle, 1); 
+  v_handle := UTL_FILE.FCLOSE(v_handle); 
+  v_handle := UTL_FILE.FOPEN(v_dirname, 
+                             v_filename, 
+                             'r', 
+                             256); 
+  v_getmsg := UTL_FILE.GET_LINE(v_handle); 
+  PERFORM DBMS_OUTPUT.PUT_LINE( 
+   'GET_MESSAGE : ' || v_getmsg); 
+  PERFORM UTL_FILE.FCLOSE_ALL(); 
+  v_opcheck := UTL_FILE.IS_OPEN(v_handle); 
+  PERFORM DBMS_OUTPUT.PUT_LINE(CASE 
+   WHEN v_opcheck IS NULL THEN 'UNKNOWN' 
+   WHEN v_opcheck THEN 'TRUE' 
+   WHEN NOT v_opcheck THEN 'FALSE' 
+   END); 
+
+ BEGIN + PERFORM UTL_FILE.PUT_LINE(v_handle, v_output); + EXCEPTION + WHEN RAISE_EXCEPTION THEN + v_errmsg := SQLERRM; + PERFORM DBMS_OUTPUT.PUT_LINE(v_errmsg); + END; +
+ EXCEPTION WHEN OTHERS THEN + PERFORM UTL_FILE.FCLOSE_ALL(); + v_errmsg := SQLERRM; + PERFORM DBMS_OUTPUT.PUT_LINE(v_errmsg); + END; + $$ + ;
+
diff --git a/contrib/orafce/doc/sql_migration/sql_migration07.md b/contrib/orafce/doc/sql_migration/sql_migration07.md new file mode 100644 index 000000000..9d6e8a34e --- /dev/null +++ b/contrib/orafce/doc/sql_migration/sql_migration07.md @@ -0,0 +1,265 @@ +Appendix A Correspondence with Oracle Databases +---- + +This appendix explains the correspondence between PostgreSQL and Oracle databases. + +### A.1 Hint Clauses + +**Description** + +An execution plan specified for a query can be controlled per SQL statement by hints without any change in the settings for the entire server. + +#### A.1.1 Hint Clause Correspondence + +The table below lists the pg_hint_plan hints that correspond to Oracle database hints. + +**Correspondence between Oracle database hints and pg_hint_plan** + +|Hint (Oracle database)|Hint (PostgreSQL)| +|:---|:---| +|FIRST_ROWS hint |Rows | +|FULL hint |Seqscan | +|INDEX hint |IndexScan | +|LEADING hint |Leading | +|NO_INDEX hint |NoIndexScan | +|NO_USE_HASH hint |NoHashJoin | +|NO_USE_MERGE hint |NoMergeJoin | +|NO_USE_NL hint |NoNestLoop | +|ORDERED hint |Leading | +|USE_HASH hint |HashJoin | +|USE_MERGE hint |MergeJoin | +|USE_NL hint |NestLoop | + +**Note** + +--- + +The optimizer operates differently for each database. Therefore, hint statements that are migrated as is may not have the same effect after migration. Be sure to verify operation at migration. + +--- + +**Migration example** + +The example below shows migration of a hint clause (INDEX hint). + + + + + + + + + + + + + + + +
Oracle databasePostgreSQL
+
SELECT /*+INDEX(inventory_table idx1)*/ *
+ FROM inventory_table WHERE i_number = 110;
+
+
SELECT /*+IndexScan(inventory_table idx1)*/ *
+ FROM inventory_table WHERE i_number = 110; 
+
+ +Note: The string idx1 is the index name defined for the i_number row of inventory_table. + +**Note** + +---- + +The pg_hint_plan hint, which is an extended feature of PostgreSQL, cannot be used to specify a column name or set a query block before the table name specification. + +---- + +### A.2 Dynamic Performance Views + +**Description** + +Dynamic performance views are views that can reference information mainly relating to database performance. + +#### A.2.1 Alternatives for Dynamic Performance Views + +PostgreSQL does not contain any dynamic performance views. Consider using the PostgreSQL system catalogs or statistics views instead. + +The table below lists the alternative system catalogs and statistics views that correspond to the dynamic performance views. + +Alternatives for dynamic performance views + +|Dynamic performance view
(Oracle database) |System catalog or statistics view
(PostgreSQL)| +|:---|:---| +|V$ACCESS | pg_locks | +|V$ACTIVE_SERVICES | pg_stat_activity | +|V$ARCHIVED_LOG | pg_stat_archiver | +|V$CLIENT_STATS | pg_stat_activity | +|V$CONTEXT | pg_settings | +|V$DATABASE | pg_database | +|V$EMX_USAGE_STATS | pg_stat_user_functions | +|V$ENABLEDPRIVS | pg_authid | +|V$ENQUEUE_LOCK | pg_locks | +|V$FILESTAT | pg_statio_all_tables | +|V$FIXED_TABLE | pg_views | +|V$FIXED_VIEW_DEFINITION | pg_views | +|V$GES_BLOCKING_ENQUEUE | pg_locks | +|V$GLOBAL_BLOCKED_LOCKS | pg_locks | +|V$GLOBAL_TRANSACTION | pg_locks | +|V$LOCK | pg_locks | +|V$LOCKED_OBJECT | pg_locks | +|V$MVREFRESH | pg_matviews | +|V$MYSTAT | pg_stat_all_tables or other view | +|V$NLS_PARAMETERS | pg_settings | +|V$NLS_VALID_VALUES | pg_proc
pg_ts_config
pg_collation
pg_type | +|V$OBJECT_PRIVILEGE | pg_default_acl | +|V$OBJECT_USAGE | pg_stat_all_indexes | +|V$OPEN_CURSOR | pg_cursors | +|V$OPTION | pg_settings | +|V$PARAMETER | pg_settings | +|V$PARAMETER2 | pg_settings | +|V$PROCESS | pg_stat_activity | +|V$PWFILE_USERS | pg_users | +|V$REPLPROP | pg_replication_origin
pg_replication_origin_status | +|V$SESSION | pg_stat_activity | +|V$SESSTAT | pg_stat_all_tables or other view | +|V$SQLFN_METADATA | pg_proc,pg_aggrgate | +|V$SYSTEM_PARAMETER | pg_settings | +|V$SYSTEM_PARAMETER2 | pg_settings | +|V$TABLESPACE | pg_tablespace | +|V$TEMPSTAT | pg_stat_database | +|V$TIMEZONE_NAMES | pg_timezone_names | +|V$TRANSACTION | pg_locks | +|V$TRANSACTION_ENQUEUE | pg_locks | + +**Note** + +---- + +Not all dynamic performance view information can be obtained from the system catalogs and statistics views. Each user should determine whether the obtained information can be used. + +---- + + +### A.3 Formats + +**Description** + +Specifying formats in data type formatting functions makes it possible to convert numeric and date and time data types to formatted strings and to convert formatted strings to specific data types. + +#### A.3.1 Number Format Correspondence + +The table below indicates which Oracle database number formats are available in PostgreSQL. + +**Number format correspondence** + +|Number format|TO_CHAR | TO_NUMBER | Remarks| +|:---|:---:|:---:|:---| +| , (comma) | Y | Y | | +| . (period) | Y | Y | | +| $ | YR | YR | If a dollar sign ($) is specified in any position other than the first character of a number format, move it to the front of the number format. | +| 0 | Y | Y | | +| 9 | Y | Y | | +| B | Y | Y | | +| C | N | N | | +| D | Y | Y | | +| EEEE | Y | Y | | +| G | Y | Y | | +| L | Y | Y | | +| MI | Y | Y | | +| PR | Y | Y | | +| RN | Y | - | | +| Rn | Y | - | | +| S | Y | Y | | +| TM | N | - | | +| U | N | N | | +| V | Y | - | | +| X | N | N | | +| X | N | N | | + +Y: Available + +YR: Available with restrictions + +N: Cannot be migrated + +-: Does not need to be migrated (because it is not available in Oracle databases) + +#### A.3.2 Datetime Format Correspondence + +The table below indicates which Oracle database datetime formats are available in PostgreSQL. + +Datetime format correspondence + +|Datetime format|TO_CHAR|TO_DATE|TO_TIMESTAMP|Remarks| +|:---|:---|:---|:---|:---| +| -
/
,
.
;
:
"text" | Y | Y | Y | | +| AD | Y | Y | Y | | +| A.D. | Y | Y | Y | | +| AM | Y | Y | Y | | +| A.M. | Y | Y | Y | | +| BC | Y | Y | Y | | +| B.C. | Y | Y | Y | | +| CC | Y | - | - | | +| SCC | Y | - | - | | +| D | Y | Y | Y | | +| DAY | Y | Y | Y | | +| DD | Y | Y | Y | | +| DDD | Y | YR | YR | The year must also be specified. (This format is used together with other formats such as YYYY.) | +| DL | N | N | N | | +| DS | N | N | N | | +| DY | Y | Y | Y | | +| E | N | N | N | | +| EE | N | N | N | | +| FF1 to FF9 | MR | - | MR | Change to MS.
However, the number of digits is fixed at three. | +| FM | YR | YR | YR | Applies only to the format specified immediately after FM. | +| FX | Y | Y | Y | | +| HH | Y | Y | Y | | +| HH12 | Y | Y | Y | | +| HH24 | Y | Y | Y | | +| IW | Y | - | - | | +| IYYY | Y | - | - | | +| IYY | Y | - | - | | +| IY | Y | - | - | | +| I | Y | - | - | | +| J | Y | Y | Y | | +| MI | Y | Y | Y | | +| MM | Y | Y | Y | | +| MON | Y | Y | Y | | +| MONTH | Y | Y | Y | | +| PM | Y | Y | Y | | +| P.M. | Y | Y | Y | | +| Q | Y | - | - | | +| RM | Y | Y | Y | | +| RR | Y | Y | Y | | +| RRRR | Y | Y | Y | | +| SS | Y | Y | Y | | +| SSSSS | M | M | M | Change to SSSS. | +| TS | N | N | N | | +| TZD | M | - | - | Change to TZ. | +| TZH | N | - | - | | +| TZM | N | - | - | | +| TZR | M | - | - | Change to TZ. | +| WW | Y | Y | Y | | +| W | Y | Y | Y | | +| X | Y | - | Y | | +| Y,YYY | Y | Y | Y | | +| YEAR | N | - | - | | +| SYEAR | N | - | - | | +| YYYY | Y | Y | Y | | +| SYYYY | N | N | N | | +| YYY | Y | Y | Y | | +| YY | Y | Y | Y | | +| Y | Y | Y | Y | | + + +Y: Available + +M: Can be migrated + +N: Cannot be migrated + +YR: Available with restrictions + +MR: Can be migrated with restrictions + +-: Does not need to be migrated (because it is not available in Oracle databases) diff --git a/contrib/orafce/expected/aggregates.out b/contrib/orafce/expected/aggregates.out new file mode 100644 index 000000000..1a4eb703b --- /dev/null +++ b/contrib/orafce/expected/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/contrib/orafce/expected/dbms_alert_session_A.out b/contrib/orafce/expected/dbms_alert_session_A.out new file mode 100644 index 000000000..534d5e304 --- /dev/null +++ b/contrib/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/contrib/orafce/expected/dbms_alert_session_B.out b/contrib/orafce/expected/dbms_alert_session_B.out new file mode 100644 index 000000000..bc170ea27 --- /dev/null +++ b/contrib/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/contrib/orafce/expected/dbms_alert_session_C.out b/contrib/orafce/expected/dbms_alert_session_C.out new file mode 100644 index 000000000..61d8d39d5 --- /dev/null +++ b/contrib/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/contrib/orafce/expected/dbms_output.out b/contrib/orafce/expected/dbms_output.out new file mode 100644 index 000000000..0ae40d42e --- /dev/null +++ b/contrib/orafce/expected/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; + 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; + 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; + 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; + buff | status +---------------+-------- + ORAFCE TEST 1 | 0 + ORA | 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; + buff | status +---------------+-------- + ORAFCE TEST 1 | 0 + | 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; + 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; + 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; + 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; + 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; + buff | status +---------------+-------- + ORAFCE TEST 1 | 1 + ORA | 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; + buff | status +---------------+-------- + ORAFCE TEST 1 | 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; + buff | status +------+-------- + ORA | 2 + FCE | 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; + 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; + buff | status +--------+-------- + | 1 + | 1 + | 1 + | 0 + | 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; + 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; + 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; + 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; + 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; + 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; + 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/contrib/orafce/expected/dbms_pipe_session_A.out b/contrib/orafce/expected/dbms_pipe_session_A.out new file mode 100644 index 000000000..5892a2ed8 --- /dev/null +++ b/contrib/orafce/expected/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/contrib/orafce/expected/dbms_pipe_session_B.out b/contrib/orafce/expected/dbms_pipe_session_B.out new file mode 100644 index 000000000..2d018d0d9 --- /dev/null +++ b/contrib/orafce/expected/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/contrib/orafce/expected/dbms_random.out b/contrib/orafce/expected/dbms_random.out new file mode 100644 index 000000000..f5498c571 --- /dev/null +++ b/contrib/orafce/expected/dbms_random.out @@ -0,0 +1,91 @@ +-- Tests for package DBMS_RANDOM +SELECT dbms_random.initialize(8); + initialize +------------ + +(1 row) + +SELECT dbms_random.normal()::numeric(10, 8); + normal +------------- + -0.37787769 +(1 row) + +SELECT dbms_random.normal()::numeric(10, 8); + normal +------------ + 0.80499804 +(1 row) + +SELECT dbms_random.seed(8); + seed +------ + +(1 row) + +SELECT dbms_random.random(); + random +------------ + -632387854 +(1 row) + +SELECT dbms_random.seed('test'); + seed +------ + +(1 row) + +SELECT dbms_random.string('U',5); + string +-------- + XEJGE +(1 row) + +SELECT dbms_random.string('P',2); + string +-------- + T9 +(1 row) + +SELECT dbms_random.string('x',4); + string +-------- + FVGL +(1 row) + +SELECT dbms_random.string('a',2); + string +-------- + AZ +(1 row) + +SELECT dbms_random.string('l',3); + string +-------- + hmo +(1 row) + +SELECT dbms_random.seed(5); + seed +------ + +(1 row) + +SELECT dbms_random.value()::numeric(10, 8); + value +------------ + 0.27474560 +(1 row) + +SELECT dbms_random.value(10,15)::numeric(10, 8); + value +------------- + 10.23233882 +(1 row) + +SELECT dbms_random.terminate(); + terminate +----------- + +(1 row) + diff --git a/contrib/orafce/expected/dbms_random_1.out b/contrib/orafce/expected/dbms_random_1.out new file mode 100644 index 000000000..12ec7547e --- /dev/null +++ b/contrib/orafce/expected/dbms_random_1.out @@ -0,0 +1,91 @@ +-- Tests for package DBMS_RANDOM +SELECT dbms_random.initialize(8); + initialize +------------ + +(1 row) + +SELECT dbms_random.normal()::numeric(10, 8); + normal +------------- + -0.37787769 +(1 row) + +SELECT dbms_random.normal()::numeric(10, 8); + normal +------------ + 0.80499804 +(1 row) + +SELECT dbms_random.seed(8); + seed +------ + +(1 row) + +SELECT dbms_random.random(); + random +------------ + -632387854 +(1 row) + +SELECT dbms_random.seed('test'); + seed +------ + +(1 row) + +SELECT dbms_random.string('U',5); + string +-------- + IGXBB +(1 row) + +SELECT dbms_random.string('P',2); + string +-------- + R` +(1 row) + +SELECT dbms_random.string('x',4); + string +-------- + P1CB +(1 row) + +SELECT dbms_random.string('a',2); + string +-------- + yV +(1 row) + +SELECT dbms_random.string('l',3); + string +-------- + ccw +(1 row) + +SELECT dbms_random.seed(5); + seed +------ + +(1 row) + +SELECT dbms_random.value()::numeric(10, 8); + value +------------ + 0.27474560 +(1 row) + +SELECT dbms_random.value(10,15)::numeric(10, 8); + value +------------- + 10.23233882 +(1 row) + +SELECT dbms_random.terminate(); + terminate +----------- + +(1 row) + diff --git a/contrib/orafce/expected/dbms_utility.out b/contrib/orafce/expected/dbms_utility.out new file mode 100644 index 000000000..dba7e5eb6 --- /dev/null +++ b/contrib/orafce/expected/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) +NOTICE: Execution time: 2 seconds diff --git a/contrib/orafce/expected/files.out b/contrib/orafce/expected/files.out new file mode 100644 index 000000000..76325585f --- /dev/null +++ b/contrib/orafce/expected/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/contrib/orafce/expected/files_1.out b/contrib/orafce/expected/files_1.out new file mode 100644 index 000000000..9dfb99768 --- /dev/null +++ b/contrib/orafce/expected/files_1.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 relation 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/contrib/orafce/expected/init.out b/contrib/orafce/expected/init.out new file mode 100644 index 000000000..25fdbb1a5 --- /dev/null +++ b/contrib/orafce/expected/init.out @@ -0,0 +1 @@ +\set ECHO none diff --git a/contrib/orafce/expected/nlssort.out b/contrib/orafce/expected/nlssort.out new file mode 100644 index 000000000..3972107fc --- /dev/null +++ b/contrib/orafce/expected/nlssort.out @@ -0,0 +1,60 @@ +-- 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/contrib/orafce/expected/nvarchar2.out b/contrib/orafce/expected/nvarchar2.out new file mode 100644 index 000000000..da580ac18 --- /dev/null +++ b/contrib/orafce/expected/nvarchar2.out @@ -0,0 +1,67 @@ +\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/contrib/orafce/expected/orafce.out b/contrib/orafce/expected/orafce.out new file mode 100644 index 000000000..8afcd0ea7 --- /dev/null +++ b/contrib/orafce/expected/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; + a | b | c | d | e +------+-----+------+------------+----- + *** | 10 | AHOJ | *** | *** + AHOJ | *** | *** | 2020-01-01 | 100 +(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; + 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/contrib/orafce/expected/orafce2.out b/contrib/orafce/expected/orafce2.out new file mode 100644 index 000000000..4b2e1e2d7 --- /dev/null +++ b/contrib/orafce/expected/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/contrib/orafce/expected/orafce2_1.out b/contrib/orafce/expected/orafce2_1.out new file mode 100644 index 000000000..ae4ecd1ce --- /dev/null +++ b/contrib/orafce/expected/orafce2_1.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/contrib/orafce/expected/regexp_func.out b/contrib/orafce/expected/regexp_func.out new file mode 100644 index 000000000..0ce4fc54e --- /dev/null +++ b/contrib/orafce/expected/regexp_func.out @@ -0,0 +1,930 @@ +-- Test for the regexp_*() function +\set ECHO none +SET search_path TO oracle,"$user", public, pg_catalog; +---- +-- Tests for REGEXP_LIKE() +---- +-- ORACLE> SELECT 1 FROM DUAL WHERE REGEXP_LIKE('a'||CHR(10)||'d', 'a.d'); -> NULL +SELECT REGEXP_LIKE('a'||CHR(10)||'d', 'a.d'); + regexp_like +------------- + f +(1 row) + +-- ORACLE> SELECT 1 FROM DUAL WHERE REGEXP_LIKE('a'||CHR(10)||'d', 'a.d', 'm'); -> NULL +SELECT REGEXP_LIKE('a'||CHR(10)||'d', 'a.d', 'm'); + regexp_like +------------- + f +(1 row) + +-- ORACLE> SELECT 1 FROM DUAL WHERE REGEXP_LIKE('a'||CHR(10)||'d', 'a.d', 'n'); -> 1 +SELECT REGEXP_LIKE('a'||CHR(10)||'d', 'a.d', 'n'); + regexp_like +------------- + t +(1 row) + +-- ORACLE> SELECT 1 FROM DUAL WHERE REGEXP_LIKE('Steven', '^Ste(v|ph)en$'); -> 1 +SELECT REGEXP_LIKE('Steven', '^Ste(v|ph)en$'); + regexp_like +------------- + t +(1 row) + +-- ORACLE> SELECT 1 FROM DUAL WHERE regexp_like('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^bar'); -> NULL +SELECT REGEXP_LIKE('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^bar'); + regexp_like +------------- + f +(1 row) + +-- ORACLE> SELECT 1 FROM DUAL WHERE regexp_like('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', 'bar'); -> 1 +SELECT REGEXP_LIKE('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', 'bar'); + regexp_like +------------- + t +(1 row) + +-- ORACLE> SELECT 1 FROM DUAL WHERE regexp_like('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^bar', 'm'); -> 1 +SELECT REGEXP_LIKE('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^bar', 'm'); + regexp_like +------------- + t +(1 row) + +-- ORACLE> SELECT 1 FROM DUAL WHERE regexp_like('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^bar', 'n'); -> NULL +SELECT REGEXP_LIKE('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^bar', 'n'); + regexp_like +------------- + f +(1 row) + +-- ORACLE> SELECT 1 FROM DUAL WHERE REGEXP_LIKE('GREEN', '([aeiou])\1'); -> NULL +SELECT REGEXP_LIKE('GREEN', '([aeiou])\1'); + regexp_like +------------- + f +(1 row) + +-- ORACLE> SELECT 1 FROM DUAL WHERE REGEXP_LIKE('GREEN', '([aeiou])\1', 'i'); -> 1 +SELECT REGEXP_LIKE('GREEN', '([aeiou])\1', 'i'); + regexp_like +------------- + t +(1 row) + +-- ORACLE> SELECT 1 FROM DUAL WHERE REGEXP_LIKE('ORANGE' || chr(10) || 'GREEN', '([aeiou])\1', 'i'); -> 1 +SELECT REGEXP_LIKE('ORANGE' || chr(10) || 'GREEN', '([aeiou])\1', 'i'); + regexp_like +------------- + t +(1 row) + +-- ORACLE> SELECT 1 FROM DUAL WHERE REGEXP_LIKE('ORANGE' || chr(10) || 'GREEN', '^..([aeiou])\1', 'i'); -> NULL +SELECT REGEXP_LIKE('ORANGE' || chr(10) || 'GREEN', '^..([aeiou])\1', 'i'); + regexp_like +------------- + f +(1 row) + +-- ORACLE> SELECT 1 FROM DUAL WHERE REGEXP_LIKE('ORANGE' || chr(10) || 'GREEN', '([aeiou])\1', 'in'); -> 1 +SELECT REGEXP_LIKE('ORANGE' || chr(10) || 'GREEN', '([aeiou])\1', 'in'); + regexp_like +------------- + t +(1 row) + +-- ORACLE> SELECT 1 FROM DUAL WHERE REGEXP_LIKE('ORANGE' || chr(10) || 'GREEN', '^..([aeiou])\1', 'in'); -> NULL +SELECT REGEXP_LIKE('ORANGE' || chr(10) || 'GREEN', '^..([aeiou])\1', 'in'); + regexp_like +------------- + f +(1 row) + +-- ORACLE> SELECT 1 FROM DUAL WHERE REGEXP_LIKE('ORANGE' || chr(10) || 'GREEN', '^..([aeiou])\1', 'im'); -> 1 +SELECT REGEXP_LIKE('ORANGE' || chr(10) || 'GREEN', '^..([aeiou])\1', 'im'); + regexp_like +------------- + t +(1 row) + +---- +-- Tests for REGEXP_COUNT() +---- +-- ORACLE> SELECT REGEXP_COUNT('a'||CHR(10)||'d', 'a.d') FROM DUAL; -> 0 +SELECT REGEXP_COUNT('a'||CHR(10)||'d', 'a.d'); + regexp_count +-------------- + 0 +(1 row) + +-- ORACLE> SELECT REGEXP_COUNT('a'||CHR(10)||'d', 'a.d', 1, 'm') FROM DUAL; -> 0 +SELECT REGEXP_COUNT('a'||CHR(10)||'d', 'a.d', 1, 'm'); + regexp_count +-------------- + 0 +(1 row) + +-- ORACLE> SELECT REGEXP_COUNT('a'||CHR(10)||'d', 'a.d', 1, 'n') FROM DUAL; -> 1 +SELECT REGEXP_COUNT('a'||CHR(10)||'d', 'a.d', 1, 'n'); + regexp_count +-------------- + 1 +(1 row) + +-- ORACLE> SELECT REGEXP_COUNT('Steven', '^Ste(v|ph)en$') FROM DUAL; -> 1 +SELECT REGEXP_COUNT('Steven', '^Ste(v|ph)en$'); + regexp_count +-------------- + 1 +(1 row) + +-- ORACLE> SELECT REGEXP_COUNT('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^bar') FROM DUAL; -> 0 +SELECT REGEXP_COUNT('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^bar'); + regexp_count +-------------- + 0 +(1 row) + +-- ORACLE> SELECT REGEXP_COUNT('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', 'bar') FROM DUAL; -> 1 +SELECT REGEXP_COUNT('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', 'bar'); + regexp_count +-------------- + 1 +(1 row) + +-- ORACLE> SELECT REGEXP_COUNT('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^bar', 0, 'm') FROM DUAL; -> ORA-01428: argument '0' is out of range +SELECT REGEXP_COUNT('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^bar', 0, 'm'); +ERROR: argument 'position' must be a number greater than 0 +-- ORACLE> SELECT REGEXP_COUNT('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^bar', 1, 'm') FROM DUAL; -> 1 +SELECT REGEXP_COUNT('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^bar', 1, 'm'); + regexp_count +-------------- + 1 +(1 row) + +-- ORACLE> SELECT REGEXP_COUNT('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^bar', 1, 'n') FROM DUAL; -> 0 +SELECT REGEXP_COUNT('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^bar', 1, 'n'); + regexp_count +-------------- + 0 +(1 row) + +-- ORACLE> SELECT REGEXP_COUNT('GREEN', '([aeiou])\1') FROM DUAL; -> 0 +SELECT REGEXP_COUNT('GREEN', '([aeiou])\1'); + regexp_count +-------------- + 0 +(1 row) + +-- ORACLE> SELECT REGEXP_COUNT('GREEN', '([aeiou])\1', 1, 'i') FROM DUAL; -> 1 +SELECT REGEXP_COUNT('GREEN', '([aeiou])\1', 1, 'i'); + regexp_count +-------------- + 1 +(1 row) + +-- ORACLE> SELECT REGEXP_COUNT('ORANGE' || chr(10) || 'GREEN', '([aeiou])\1', 1, 'i') FROM DUAL; -> 1 +SELECT REGEXP_COUNT('ORANGE' || chr(10) || 'GREEN', '([aeiou])\1', 1, 'i'); + regexp_count +-------------- + 1 +(1 row) + +-- ORACLE> SELECT REGEXP_COUNT('ORANGE' || chr(10) || 'GREEN', '^..([aeiou])\1', 1, 'i') FROM DUAL; -> 0 +SELECT REGEXP_COUNT('ORANGE' || chr(10) || 'GREEN', '^..([aeiou])\1', 1, 'i'); + regexp_count +-------------- + 0 +(1 row) + +-- ORACLE> SELECT REGEXP_COUNT('ORANGE' || chr(10) || 'GREEN', '([aeiou])\1', 1, 'in') FROM DUAL; -> 1 +SELECT REGEXP_COUNT('ORANGE' || chr(10) || 'GREEN', '([aeiou])\1', 1, 'in'); + regexp_count +-------------- + 1 +(1 row) + +-- ORACLE> SELECT REGEXP_COUNT('ORANGE' || chr(10) || 'GREEN', '^..([aeiou])\1', 1, 'in') FROM DUAL; -> 0 +SELECT REGEXP_COUNT('ORANGE' || chr(10) || 'GREEN', '^..([aeiou])\1', 1, 'in'); + regexp_count +-------------- + 0 +(1 row) + +-- ORACLE> SELECT REGEXP_COUNT('ORANGE' || chr(10) || 'GREEN', '^..([aeiou])\1', 1, 'im') FROM DUAL; -> 1 +SELECT REGEXP_COUNT('ORANGE' || chr(10) || 'GREEN', '^..([aeiou])\1', 1, 'im'); + regexp_count +-------------- + 1 +(1 row) + +-- ORACLE> SELECT REGEXP_COUNT('123123123123123', '(12)3', 1, 'i') REGEXP_COUNT FROM DUAL; -> 5 +SELECT REGEXP_COUNT('123123123123123', '(12)3', 1, 'i'); + regexp_count +-------------- + 5 +(1 row) + +-- ORACLE> SELECT REGEXP_COUNT('123123123123', '123', 3, 'i') COUNT FROM DUAL; -> 3 +SELECT REGEXP_COUNT('123123123123', '123', 3, 'i'); + regexp_count +-------------- + 3 +(1 row) + +-- ORACLE> SELECT REGEXP_COUNT('ABC123', '[A-Z]'), REGEXP_COUNT('A1B2C3', '[A-Z]') FROM DUAL; -> 3 | 3 +SELECT REGEXP_COUNT('ABC123', '[A-Z]'), oracle.REGEXP_COUNT('A1B2C3', '[A-Z]'); + regexp_count | regexp_count +--------------+-------------- + 3 | 3 +(1 row) + +-- ORACLE> SELECT REGEXP_COUNT('ABC123', '[A-Z][0-9]'), REGEXP_COUNT('A1B2C3', '[A-Z][0-9]') FROM DUAL; -> 1 | 3 +SELECT REGEXP_COUNT('ABC123', '[A-Z][0-9]'), oracle.REGEXP_COUNT('A1B2C3', '[A-Z][0-9]'); + regexp_count | regexp_count +--------------+-------------- + 1 | 3 +(1 row) + +-- ORACLE> SELECT REGEXP_COUNT('ABC123', '^[A-Z][0-9]'), REGEXP_COUNT('A1B2C3', '^[A-Z][0-9]') FROM DUAL; -> 0 | 1 +SELECT REGEXP_COUNT('ABC123', '^[A-Z][0-9]'), oracle.REGEXP_COUNT('A1B2C3', '^[A-Z][0-9]'); + regexp_count | regexp_count +--------------+-------------- + 0 | 1 +(1 row) + +-- ORACLE> SELECT REGEXP_COUNT('ABC123', '([A-Z][0-9]){2}'), REGEXP_COUNT('A1B2C3', '([A-Z][0-9]){2}') FROM DUAL; -> 0 | 1 +SELECT REGEXP_COUNT('ABC123', '([A-Z][0-9]){2}'), oracle.REGEXP_COUNT('A1B2C3', '([A-Z][0-9]){2}'); + regexp_count | regexp_count +--------------+-------------- + 0 | 1 +(1 row) + +-- Check negatives values that must throw an error +SELECT REGEXP_COUNT('ORANGE' || chr(10) || 'GREEN', '^..([aeiou])\1', -1, 'i'); +ERROR: argument 'position' must be a number greater than 0 +---- +-- Tests for REGEXP_INSTR() +---- +-- ORACLE> SELECT REGEXP_INSTR('1234567890', '(123)(4(56)(78))') FROM DUAL; -> 1 +SELECT REGEXP_INSTR('1234567890', '(123)(4(56)(78))'); + regexp_instr +-------------- + 1 +(1 row) + +-- ORACLE> SELECT REGEXP_INSTR('1234567890', '(4(56)(78))') FROM DUAL; -> 4 +SELECT REGEXP_INSTR('1234567890', '(4(56)(78))'); + regexp_instr +-------------- + 4 +(1 row) + +-- ORACLE> SELECT regexp_instr('1234567890', '123(4(56)(78))', 3) FROM DUAL; -> 0 +SELECT REGEXP_INSTR('1234567890', '(123)(4(56)(78))', 3); + regexp_instr +-------------- + 0 +(1 row) + +-- ORACLE> SELECT REGEXP_INSTR('1234567890', '(4(56)(78))', 3) FROM DUAL; -> 4 +SELECT REGEXP_INSTR('1234567890', '(4(56)(78))', 3); + regexp_instr +-------------- + 4 +(1 row) + +-- ORACLE> SELECT REGEXP_INSTR('500 Oracle Parkway, Redwood Shores, CA', '[^ ]+', 1, 6) FROM DUAL; -> 37 +SELECT REGEXP_INSTR('500 Oracle Parkway, Redwood Shores, CA', '[^ ]+', 1, 6); + regexp_instr +-------------- + 37 +(1 row) + +-- ORACLE> SELECT REGEXP_INSTR('500 Oracle Parkway, Redwood Shores, CA', '[S|R|P][[:alpha:]]{6}', 3, 2, 0) FROM DUAL; -> 21 +SELECT REGEXP_INSTR('500 Oracle Parkway, Redwood Shores, CA', '[S|R|P][[:alpha:]]{6}', 3, 2, 0); + regexp_instr +-------------- + 21 +(1 row) + +-- ORACLE> SELECT REGEXP_INSTR('500 Oracle Parkway, Redwood Shores, CA', '[S|R|P][[:alpha:]]{6}', 3, 2, 1) FROM DUAL; -> 28 +SELECT REGEXP_INSTR('500 Oracle Parkway, Redwood Shores, CA', '[S|R|P][[:alpha:]]{6}', 3, 2, 1); + regexp_instr +-------------- + 28 +(1 row) + +-- ORACLE> SELECT REGEXP_INSTR('500 Oracle Parkway, Redwood Shores, CA', '[s|r|p][[:alpha:]]{6}', 3, 2, 1, 'i') FROM DUAL; -> 28 +SELECT REGEXP_INSTR('500 Oracle Parkway, Redwood Shores, CA', '[q|r|p][[:alpha:]]{6}', 3, 2, 1, 'i'); + regexp_instr +-------------- + 28 +(1 row) + +-- ORACLE> SELECT REGEXP_INSTR('1234567890', '(123)(4(56)(78))', 1, 1, 0, 'i', 0) FROM DUAL; -> 1 +SELECT REGEXP_INSTR('1234567890', '(123)(4(56)(78))', 1, 1, 0, 'i', 0); + regexp_instr +-------------- + 1 +(1 row) + +-- ORACLE> SELECT REGEXP_INSTR('1234567890', '(123)(4(56)(78))', 1, 1, 0, 'i', 1) FROM DUAL; -> 1 +SELECT REGEXP_INSTR('1234567890', '(123)(4(56)(78))', 1, 1, 0, 'i', 1); + regexp_instr +-------------- + 1 +(1 row) + +-- ORACLE> SELECT REGEXP_INSTR('1234567890', '(123)(4(56)(78))', 1, 1, 0, 'i', 2) FROM DUAL; -> 4 +SELECT REGEXP_INSTR('1234567890', '(123)(4(56)(78))', 1, 1, 0, 'i', 2); + regexp_instr +-------------- + 4 +(1 row) + +-- ORACLE> SELECT REGEXP_INSTR('1234567890', '(123)(4(56)(78))', 1, 1, 0, 'i', 4) FROM DUAL; -> 7 +SELECT REGEXP_INSTR('1234567890', '(123)(4(56)(78))', 1, 1, 0, 'i', 4); + regexp_instr +-------------- + 7 +(1 row) + +-- ORACLE> SELECT REGEXP_INSTR('1234567890 1234567890', '(123)(4(56)(78))', 1, 1, 0, 'i', 4) FROM DUAL; -> 7 +SELECT REGEXP_INSTR('1234567890 1234567890', '(123)(4(56)(78))', 1, 1, 0, 'i', 4); + regexp_instr +-------------- + 7 +(1 row) + +-- ORACLE> SELECT REGEXP_INSTR('1234567890 1234567890', '(123)(4(56)(78))', 1, 2, 0, 'i', 4) FROM DUAL; -> 18 +SELECT REGEXP_INSTR('1234567890 1234567890', '(123)(4(56)(78))', 1, 2, 0, 'i', 4); + regexp_instr +-------------- + 18 +(1 row) + +-- ORACLE> SELECT REGEXP_INSTR('1234567890 1234567890', '(123)(4(56)(78))', 1, 2, 1, 'i', 4) FROM DUAL; -> 20 +SELECT REGEXP_INSTR('1234567890 1234567890', '(123)(4(56)(78))', 1, 2, 1, 'i', 4); + regexp_instr +-------------- + 20 +(1 row) + +-- ORACLE> SELECT REGEXP_INSTR('1234567890 1234567890 1234567890', '(123)(4(56)(78))', 1, 3, 0,'i', 4) FROM DUAL; -> 29 +SELECT REGEXP_INSTR('1234567890 1234567890 1234567890', '(123)(4(56)(78))', 1, 3, 0, 'i', 4); + regexp_instr +-------------- + 29 +(1 row) + +-- ORACLE> SELECT REGEXP_INSTR('1234567890 1234567890 1234567890', '(123)(4(56)(78))', 1, 3, 1,'i', 4) FROM DUAL; -> 31 +SELECT REGEXP_INSTR('1234567890 1234567890 1234567890', '(123)(4(56)(78))', 1, 3, 1, 'i', 4); + regexp_instr +-------------- + 31 +(1 row) + +-- DROP TABLE regexp_temp; +-- CREATE TABLE regexp_temp(empName varchar2(20), emailID varchar2(20)); +-- INSERT INTO regexp_temp (empName, emailID) VALUES ('John Doe', 'johndoe@example.com'); +-- INSERT INTO regexp_temp (empName, emailID) VALUES ('Jane Doe', 'janedoe'); +-- COMMIT; +CREATE TEMPORARY TABLE regexp_temp(empName varchar(20), emailID varchar(20)); +INSERT INTO regexp_temp (empName, emailID) VALUES ('John Doe', 'johndoe@example.com'); +INSERT INTO regexp_temp (empName, emailID) VALUES ('Jane Doe', 'janedoe'); +-- -- ORACLE> SELECT emailID, REGEXP_INSTR(emailID, '\w+@\w+(\.\w+)+') "IS_A_VALID_EMAIL" FROM regexp_temp; +-- EMAILID IS_A_VALID_EMAIL +-- -------------------- ---------------- +-- johndoe@example.com 1 +-- example.com 0 +SELECT emailID, REGEXP_INSTR(emailID, '\w+@\w+(\.\w+)+') FROM regexp_temp; + emailid | regexp_instr +---------------------+-------------- + johndoe@example.com | 1 + janedoe | 0 +(2 rows) + +-- -- ORACLE> SELECT emailID, REGEXP_INSTR(emailID, '\w+@\w+(\.\w+)+', 1, 1, 0, 'i', 0) "IS_A_VALID_EMAIL" FROM regexp_temp; +-- EMAILID IS_A_VALID_EMAIL +-- -------------------- ---------------- +-- johndoe@example.com 1 +-- example.com 0 +SELECT emailID, REGEXP_INSTR(emailID, '\w+@\w+(\.\w+)+', 1, 1, 0, 'i', 0) FROM regexp_temp; + emailid | regexp_instr +---------------------+-------------- + johndoe@example.com | 1 + janedoe | 0 +(2 rows) + +-- -- ORACLE> SELECT emailID, REGEXP_INSTR(emailID, '\w+@\w+(\.\w+)+', 1, 1, 0, 'i', 1) "IS_A_VALID_EMAIL" FROM regexp_temp; +-- EMAILID IS_A_VALID_EMAIL +-- -------------------- ---------------- +-- johndoe@example.com 16 +-- example.com 0 +SELECT emailID, REGEXP_INSTR(emailID, '\w+@\w+(\.\w+)+', 1, 1, 0, 'i', 1) FROM regexp_temp; + emailid | regexp_instr +---------------------+-------------- + johndoe@example.com | 16 + janedoe | 0 +(2 rows) + +DROP TABLE regexp_temp; +-- Check negatives values that must throw an error +SELECT REGEXP_INSTR('1234567890 1234567890 1234567890', '(123)(4(56)(78))', -1, 3, 1, 'i', 4); +ERROR: argument 'position' must be a number greater than 0 +SELECT REGEXP_INSTR('1234567890 1234567890 1234567890', '(123)(4(56)(78))', 1, -3, 1, 'i', 4); +ERROR: argument 'occurence' must be a number greater than 0 +SELECT REGEXP_INSTR('1234567890 1234567890 1234567890', '(123)(4(56)(78))', 1, 3, -1, 'i', 4); +ERROR: argument 'return_opt' must be 0 or 1 +SELECT REGEXP_INSTR('1234567890 1234567890 1234567890', '(123)(4(56)(78))', 1, 3, 1, 'i', -4); +ERROR: argument 'group' must be a positive number +---- +-- Tests for REGEXP_SUBSTR() +---- +-- ORACLE> SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',[^,]+') FROM DUAL; -> , zipcode town +SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',[^,]+'); + regexp_substr +---------------- + , zipcode town +(1 row) + +-- ORACLE> SELECT REGEXP_SUBSTR('http://www.example.com/products', 'http://([[:alnum:]]+\.?){3,4}/?') FROM DUAL; -> http://www.example.com/ +SELECT REGEXP_SUBSTR('http://www.example.com/products', 'http://([[:alnum:]]+\.?){3,4}/?'); + regexp_substr +------------------------- + http://www.example.com/ +(1 row) + +-- ORACLE> SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',[^,]+', 24) FROM DUAL; -> , FR +SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',[^,]+', 24); + regexp_substr +--------------- + , FR +(1 row) + +-- ORACLE> SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',[^,]+', 1, 1) FROM DUAL; -> , zipcode town +SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',[^,]+', 1, 1); + regexp_substr +---------------- + , zipcode town +(1 row) + +-- ORACLE> SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',[^,]+', 1, 2) FROM DUAL; -> , FR +SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',[^,]+', 1, 2); + regexp_substr +--------------- + , FR +(1 row) + +-- ORACLE> SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',\s+[Zf][^,]+', 1, 1) FROM DUAL; -> NULL +SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',\s+[Zf][^,]+', 1, 1); + regexp_substr +--------------- + +(1 row) + +-- ORACLE> SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',\s+[Zf][^,]+', 1, 1, 'i') FROM DUAL; -> , zipcode town +SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',\s+[Zf][^,]+', 1, 1, 'i'); + regexp_substr +---------------- + , zipcode town +(1 row) + +-- ORACLE> SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',\s+[Zf][^,]+', 1, 1, 'i', 0) FROM DUAL; -> , zipcode town +SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',\s+[Zf][^,]+', 1, 1, 'i', 0); + regexp_substr +---------------- + , zipcode town +(1 row) + +-- ORACLE> SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',\s+[Zf][^,]+', 1, 1, 'i', 1) FROM DUAL; -> NULL +SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',\s+[Zf][^,]+', 1, 1, 'i', 1); + regexp_substr +--------------- + +(1 row) + +-- ORACLE> SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',\s+([Zf][^,]+)', 1, 1, 'i', 1) FROM DUAL; -> zipcode town +SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',\s+([Zf][^,]+)', 1, 1, 'i', 1); + regexp_substr +--------------- + zipcode town +(1 row) + +-- ORACLE> SELECT REGEXP_SUBSTR('1234567890 1234567890', '(123)(4(56)(78))', 1, 1, 'i', 4) FROM DUAL; -> 78 +SELECT REGEXP_SUBSTR('1234567890 1234567890', '(123)(4(56)(78))', 1, 1, 'i', 4); + regexp_substr +--------------- + 78 +(1 row) + +-- ORACLE> SELECT REGEXP_SUBSTR('1234567890 1234557890', '(123)(4(5[56])(78))', 1, 2, 'i', 3) FROM DUAL; -> 55 +SELECT REGEXP_SUBSTR('1234567890 1234557890', '(123)(4(5[56])(78))', 1, 2, 'i', 3); + regexp_substr +--------------- + 55 +(1 row) + +-- ORACLE> SELECT REGEXP_SUBSTR('1234567890 1234567890', '(123)(4(56)(78))', 1, 1, 'i', 0) FROM DUAL; -> 12345678 +SELECT REGEXP_SUBSTR('1234567890 1234567890', '(123)(4(56)(78))', 1, 1, 'i', 0); + regexp_substr +--------------- + 12345678 +(1 row) + +-- Check negatives values that must throw an error +SELECT REGEXP_SUBSTR('1234567890 1234567890', '(123)(4(56)(78))', -1, 1, 'i', 0); +ERROR: argument 'position' must be a number greater than 0 +SELECT REGEXP_SUBSTR('1234567890 1234567890', '(123)(4(56)(78))', 1, -1, 'i', 0); +ERROR: argument 'occurence' must be a number greater than 0 +SELECT REGEXP_SUBSTR('1234567890 1234567890', '(123)(4(56)(78))', 1, 1, 'i', -1); +ERROR: argument 'group' must be a positive number +---- +-- Tests for REGEXP_REPLACE() +---- +-- ORACLE> SELECT REGEXP_REPLACE('512.123.4567', '([[:digit:]]{3})\.([[:digit:]]{3})\.([[:digit:]]{4})', '(\1) \2-\3') FROM DUAL; -> (512) 123-4567 +SELECT REGEXP_REPLACE('512.123.4567', '([[:digit:]]{3})\.([[:digit:]]{3})\.([[:digit:]]{4})', '(\1) \2-\3'); + regexp_replace +---------------- + (512) 123-4567 +(1 row) + +-- ORACLE> SELECT REGEXP_REPLACE('512.123.4567 612.123.4567', '([[:digit:]]{3})\.([[:digit:]]{3})\.([[:digit:]]{4})', '(\1) \2-\3') FROM DUAL; -> (512) 123-4567 (612) 123-4567 +SELECT oracle.REGEXP_REPLACE('512.123.4567 612.123.4567', '([[:digit:]]{3})\.([[:digit:]]{3})\.([[:digit:]]{4})', '(\1) \2-\3'); + regexp_replace +------------------------------- + (512) 123-4567 (612) 123-4567 +(1 row) + +-- ORACLE> SELECT REGEXP_REPLACE('number your street, zipcode town, FR', '( ){2,}', ' ') FROM DUAL; -> number your street, zipcode town, FR +SELECT oracle.REGEXP_REPLACE('number your street, zipcode town, FR', '( ){2,}', ' '); + regexp_replace +-------------------------------------- + number your street, zipcode town, FR +(1 row) + +-- ORACLE> SELECT REGEXP_REPLACE('number your street,'||CHR(10)||' zipcode town, FR', '( ){2,}', ' ') FROM DUAL; -> number your street, +-- zipcode town, FR +SELECT oracle.REGEXP_REPLACE('number your street,'||CHR(10)||' zipcode town, FR', '( ){2,}', ' '); + regexp_replace +--------------------- + number your street,+ + zipcode town, FR +(1 row) + +-- ORACLE> SELECT REGEXP_REPLACE('number your street, zipcode town, FR', '( ){2,}', ' ', 9) FROM DUAL; -> number your street, zipcode town, FR +SELECT oracle.REGEXP_REPLACE('number your street, zipcode town, FR', '( ){2,}', ' ', 9); + regexp_replace +---------------------------------------- + number your street, zipcode town, FR +(1 row) + +-- ORACLE> SELECT REGEXP_REPLACE('number your street, zipcode town, FR', '( ){2,}', ' ', 9, 0) FROM DUAL; -> number your street, zipcode town, FR +SELECT oracle.REGEXP_REPLACE('number your street, zipcode town, FR', '( ){2,}', ' ', 9, 0); + regexp_replace +---------------------------------------- + number your street, zipcode town, FR +(1 row) + +-- ORACLE> SELECT REGEXP_REPLACE('number your street, zipcode town, FR', '( ){2,}', ' ', 9, 2) FROM DUAL; -> number your street, zipcode town, FR +SELECT oracle.REGEXP_REPLACE('number your street, zipcode town, FR', '( ){2,}', ' ', 9, 2); + regexp_replace +--------------------------------------------- + number your street, zipcode town, FR +(1 row) + +-- ORACLE> SELECT REGEXP_REPLACE('number your street, zipcode town, FR', '( ){2,}', ' ', 9, 2, 'm') FROM DUAL; -> number your street, zipcode town, FR +SELECT oracle.REGEXP_REPLACE('number your street, zipcode town, FR', '( ){2,}', ' ', 9, 2, 'm'); + regexp_replace +--------------------------------------------- + number your street, zipcode town, FR +(1 row) + +-- ORACLE> SELECT REGEXP_REPLACE('number your street, zipcode town, FR', '([EURT]){2,}', '[\1]', 9, 2, 'i') FROM DUAL; -> number your s[t], zipcode town, FR +SELECT oracle.REGEXP_REPLACE('number your street, zipcode town, FR', '([EURT]){2,}', '[\1]', 9, 2, 'i'); + regexp_replace +---------------------------------------------- + number your s[t], zipcode town, FR +(1 row) + +-- ORACLE> SELECT REGEXP_REPLACE('number your street, zipcode town, FR', '[ ]{2,}', ' ', 9, 2) FROM DUAL; -> number your street, zipcode town, FR +SELECT oracle.REGEXP_REPLACE('number your street, zipcode town, FR', '( ){2,}', ' ', 9, 2); + regexp_replace +--------------------------------------------- + number your street, zipcode town, FR +(1 row) + +-- ORACLE> SELECT REGEXP_REPLACE ('A PostgreSQL function', 'A|e|i|o|u', 'X', 1, 2) FROM DUAL; -> A PXstgreSQL function +SELECT oracle.REGEXP_REPLACE ('A PostgreSQL function', 'A|e|i|o|u', 'X', 1, 2); + regexp_replace +----------------------- + A PXstgreSQL function +(1 row) + +-- ORACLE> SELECT REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', 1, 0, 'i') FROM DUAL; -> X PXstgrXSQL fXnctXXn +SELECT oracle.REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', 1, 0, 'i'); + regexp_replace +----------------------- + X PXstgrXSQL fXnctXXn +(1 row) + +-- ORACLE> SELECT REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', 1, 1, 'i') FROM DUAL; -> X PostgreSQL function +SELECT oracle.REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', 1, 1, 'i'); + regexp_replace +----------------------- + X PostgreSQL function +(1 row) + +-- ORACLE> SELECT REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', 1, 2, 'i') FROM DUAL; -> A PXstgreSQL function +SELECT oracle.REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', 1, 2, 'i'); + regexp_replace +----------------------- + A PXstgreSQL function +(1 row) + +-- ORACLE> SELECT REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', 1, 3, 'i') FROM DUAL; -> A PostgrXSQL function +SELECT oracle.REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', 1, 3, 'i'); + regexp_replace +----------------------- + A PostgrXSQL function +(1 row) + +-- ORACLE> SELECT REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', 1, 9, 'i') FROM DUAL; -> A PostgreSQL function +SELECT oracle.REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', 1, 9, 'i'); + regexp_replace +----------------------- + A PostgreSQL function +(1 row) + +-- ORACLE> SELECT REGEXP_REPLACE ('A PostgreSQL function', 'A|e|i|o|u', 'X', 1, 9) FROM DUAL; -> A PostgreSQL function +SELECT oracle.REGEXP_REPLACE ('A PostgreSQL function', 'A|e|i|o|u', 'X', 1, 9); + regexp_replace +----------------------- + A PostgreSQL function +(1 row) + +-- ORACLE> SELECT REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', -1, 0, 'i') FROM DUAL; -> ORA-01428 +SELECT oracle.REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', -1, 0, 'i'); +ERROR: argument 'position' must be a number greater than 0 +-- ORACLE> SELECT REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', 1, -1, 'i') FROM DUAL; -> ORA-01428 +SELECT oracle.REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', 1, -1, 'i'); +ERROR: argument 'occurrence' must be a positive number +-- ORACLE> SELECT REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', 1, 1, 'g') FROM DUAL; -> ORA-01760 +SELECT oracle.REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', 1, 1, 'g'); +ERROR: argument 'flags' has unsupported modifier(s). +-- +-- Test NULL input in the regexp_* functions that must returned NULL except for the modifier +-- or regexp flag. There is an exception with regexp_replace(), if the pattern is null (second +-- parameter) the original string is returned. We don't test functions witht the STRICT attribute +-- +SELECT oracle.REGEXP_LIKE(NULL, '\d+', 'i'); + regexp_like +------------- + +(1 row) + +SELECT oracle.REGEXP_LIKE('1234', NULL, 'i'); + regexp_like +------------- + +(1 row) + +SELECT oracle.REGEXP_LIKE('1234', '\d+', NULL); + regexp_like +------------- + t +(1 row) + +SELECT oracle.REGEXP_LIKE('1234', '\d+', ''); + regexp_like +------------- + t +(1 row) + +SELECT oracle.REGEXP_COUNT('1234', '\d', NULL) ; + regexp_count +-------------- + +(1 row) + +SELECT oracle.REGEXP_COUNT('1234', '\d', 1, NULL) ; + regexp_count +-------------- + 4 +(1 row) + +SELECT oracle.REGEXP_COUNT('1234', '\d', 1, '') ; + regexp_count +-------------- + 4 +(1 row) + +SELECT oracle.REGEXP_COUNT('1234', '\d', NULL, NULL) ; + regexp_count +-------------- + +(1 row) + +SELECT oracle.REGEXP_COUNT(NULL, '4', 1, 'i'); + regexp_count +-------------- + +(1 row) + +SELECT oracle.REGEXP_INSTR('1234', '4', NULL); + regexp_instr +-------------- + +(1 row) + +SELECT oracle.REGEXP_INSTR('1234', '4', 1, NULL); + regexp_instr +-------------- + +(1 row) + +SELECT oracle.REGEXP_INSTR('1234', '4', 1, 1, NULL); + regexp_instr +-------------- + +(1 row) + +SELECT oracle.REGEXP_INSTR('1234', '4', 1, 1, 1, NULL); + regexp_instr +-------------- + 5 +(1 row) + +SELECT oracle.REGEXP_INSTR('1234', '4', 1, 1, 0, NULL); + regexp_instr +-------------- + 4 +(1 row) + +SELECT oracle.REGEXP_INSTR('1234', '4', 1, 1, 0, 'i', NULL); + regexp_instr +-------------- + +(1 row) + +SELECT oracle.REGEXP_INSTR('1234', '4', 1, 1, 0, '', NULL); + regexp_instr +-------------- + +(1 row) + +SELECT oracle.REGEXP_INSTR(NULL, '4', 1, 1, 0, 'i', 2); + regexp_instr +-------------- + +(1 row) + +SELECT oracle.REGEXP_INSTR(NULL, '4', 1, 1, 0, 'i', 2); + regexp_instr +-------------- + +(1 row) + +SELECT oracle.REGEXP_SUBSTR('1234', '1(.*)', null); + regexp_substr +--------------- + +(1 row) + +SELECT oracle.REGEXP_SUBSTR('1234', '234', 1, null); + regexp_substr +--------------- + +(1 row) + +SELECT oracle.REGEXP_SUBSTR('1234', '234', 1, 1, null); + regexp_substr +--------------- + 234 +(1 row) + +SELECT oracle.REGEXP_SUBSTR('1234', '234', 1, 1, ''); + regexp_substr +--------------- + 234 +(1 row) + +SELECT oracle.REGEXP_SUBSTR('1234', '234', 1, 1, 'i', null); + regexp_substr +--------------- + +(1 row) + +-- test for capture group +SELECT oracle.REGEXP_SUBSTR('1234', '2(3)(4)', 1, 1, 'i', 1); + regexp_substr +--------------- + 3 +(1 row) + +SELECT oracle.REGEXP_SUBSTR('1234', '2(3)(4)', 1, 1, 'i', 2); + regexp_substr +--------------- + 4 +(1 row) + +SELECT oracle.REGEXP_SUBSTR('1234', '2(3)(4)', 1, 1, 'i', 0); + regexp_substr +--------------- + 234 +(1 row) + +-- Special case for second parameter in REGEXP_REPLACE, when null returns the original value. +SELECT oracle.REGEXP_REPLACE(null, '\d', 'a'); + regexp_replace +---------------- + +(1 row) + +SELECT oracle.REGEXP_REPLACE('1234', null, 'a'); + regexp_replace +---------------- + 1234 +(1 row) + +SELECT oracle.REGEXP_REPLACE('1234', null, null); + regexp_replace +---------------- + 1234 +(1 row) + +SELECT oracle.REGEXP_REPLACE('1234', '\d', null); + regexp_replace +---------------- + +(1 row) + +SELECT oracle.REGEXP_REPLACE('1234', '\d', 'a', null); + regexp_replace +---------------- + +(1 row) + +SELECT oracle.REGEXP_REPLACE('1234', null, 'a', 2); + regexp_replace +---------------- + 1234 +(1 row) + +SELECT oracle.REGEXP_REPLACE('1234', null, 'a', null); + regexp_replace +---------------- + +(1 row) + +SELECT oracle.REGEXP_REPLACE('1234', null, 'a', 1); + regexp_replace +---------------- + 1234 +(1 row) + +SELECT oracle.REGEXP_REPLACE('1234', null, 'a', 1, null); + regexp_replace +---------------- + +(1 row) + +SELECT oracle.REGEXP_REPLACE('1234', '\d', 'a', 1, null); + regexp_replace +---------------- + +(1 row) + +SELECT oracle.REGEXP_REPLACE('1234', '\d', 'a', 1, null); + regexp_replace +---------------- + +(1 row) + +SELECT oracle.REGEXP_REPLACE('1234', '\d', 'a', 1, null); + regexp_replace +---------------- + +(1 row) + +SELECT oracle.REGEXP_REPLACE('1234', '\d', 'a', 1, 1, null); + regexp_replace +---------------- + a234 +(1 row) + +SELECT oracle.REGEXP_REPLACE('1234', '\d', 'a', 1, NULL, 'i'); + regexp_replace +---------------- + +(1 row) + +SELECT oracle.REGEXP_REPLACE('1234', null, 'a', 1, 1, 'i'); + regexp_replace +---------------- + 1234 +(1 row) + diff --git a/contrib/orafce/expected/varchar2.out b/contrib/orafce/expected/varchar2.out new file mode 100644 index 000000000..c5fa4a272 --- /dev/null +++ b/contrib/orafce/expected/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); +-- +-- 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/contrib/orafce/file.c b/contrib/orafce/file.c new file mode 100644 index 000000000..f721414ab --- /dev/null +++ b/contrib/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/contrib/orafce/magic.c b/contrib/orafce/magic.c new file mode 100644 index 000000000..acc21a456 --- /dev/null +++ b/contrib/orafce/magic.c @@ -0,0 +1,6 @@ +#include "postgres.h" +#include "fmgr.h" + +#ifdef PG_MODULE_MAGIC +PG_MODULE_MAGIC; +#endif diff --git a/contrib/orafce/msvc/orafce.2010.sln b/contrib/orafce/msvc/orafce.2010.sln new file mode 100644 index 000000000..0b815ff4b --- /dev/null +++ b/contrib/orafce/msvc/orafce.2010.sln @@ -0,0 +1,44 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "orafce", "orafce.2010.vcxproj", "{B6B37F22-9E44-4240-AAA0-650D4AC2C2E2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + 10.3|Win32 = 10.3|Win32 + 10.3|x64 = 10.3|x64 + 8.3|Win32 = 8.3|Win32 + 8.3|x64 = 8.3|x64 + 8.4|Win32 = 8.4|Win32 + 8.4|x64 = 8.4|x64 + 9.0|Win32 = 9.0|Win32 + 9.0|x64 = 9.0|x64 + 9.6|Win32 = 9.6|Win32 + 9.6|x64 = 9.6|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B6B37F22-9E44-4240-AAA0-650D4AC2C2E2}.10.3|Win32.ActiveCfg = 10.3|Win32 + {B6B37F22-9E44-4240-AAA0-650D4AC2C2E2}.10.3|Win32.Build.0 = 10.3|Win32 + {B6B37F22-9E44-4240-AAA0-650D4AC2C2E2}.10.3|x64.ActiveCfg = 10.3|x64 + {B6B37F22-9E44-4240-AAA0-650D4AC2C2E2}.10.3|x64.Build.0 = 10.3|x64 + {B6B37F22-9E44-4240-AAA0-650D4AC2C2E2}.8.3|Win32.ActiveCfg = 8.3|Win32 + {B6B37F22-9E44-4240-AAA0-650D4AC2C2E2}.8.3|Win32.Build.0 = 8.3|Win32 + {B6B37F22-9E44-4240-AAA0-650D4AC2C2E2}.8.3|x64.ActiveCfg = 8.3|x64 + {B6B37F22-9E44-4240-AAA0-650D4AC2C2E2}.8.3|x64.Build.0 = 8.3|x64 + {B6B37F22-9E44-4240-AAA0-650D4AC2C2E2}.8.4|Win32.ActiveCfg = 8.4|Win32 + {B6B37F22-9E44-4240-AAA0-650D4AC2C2E2}.8.4|Win32.Build.0 = 8.4|Win32 + {B6B37F22-9E44-4240-AAA0-650D4AC2C2E2}.8.4|x64.ActiveCfg = 8.4|x64 + {B6B37F22-9E44-4240-AAA0-650D4AC2C2E2}.8.4|x64.Build.0 = 8.4|x64 + {B6B37F22-9E44-4240-AAA0-650D4AC2C2E2}.9.0|Win32.ActiveCfg = 9.0|Win32 + {B6B37F22-9E44-4240-AAA0-650D4AC2C2E2}.9.0|Win32.Build.0 = 9.0|Win32 + {B6B37F22-9E44-4240-AAA0-650D4AC2C2E2}.9.0|x64.ActiveCfg = 9.0|x64 + {B6B37F22-9E44-4240-AAA0-650D4AC2C2E2}.9.0|x64.Build.0 = 9.0|x64 + {B6B37F22-9E44-4240-AAA0-650D4AC2C2E2}.9.6|Win32.ActiveCfg = 9.6|Win32 + {B6B37F22-9E44-4240-AAA0-650D4AC2C2E2}.9.6|Win32.Build.0 = 9.6|Win32 + {B6B37F22-9E44-4240-AAA0-650D4AC2C2E2}.9.6|x64.ActiveCfg = 9.6|x64 + {B6B37F22-9E44-4240-AAA0-650D4AC2C2E2}.9.6|x64.Build.0 = 9.6|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/contrib/orafce/msvc/orafce.2010.vcxproj b/contrib/orafce/msvc/orafce.2010.vcxproj new file mode 100644 index 000000000..8efe13d16 --- /dev/null +++ b/contrib/orafce/msvc/orafce.2010.vcxproj @@ -0,0 +1,523 @@ + + + + + 10.3 + Win32 + + + 10.3 + x64 + + + 8.3 + Win32 + + + 8.3 + x64 + + + 8.4 + Win32 + + + 8.4 + x64 + + + 9.0 + Win32 + + + 9.0 + x64 + + + 9.6 + Win32 + + + 9.6 + x64 + + + + {B6B37F22-9E44-4240-AAA0-650D4AC2C2E2} + lib + Win32Proj + orafce + + + + DynamicLibrary + Unicode + true + Windows7.1SDK + + + DynamicLibrary + Unicode + true + Windows7.1SDK + + + DynamicLibrary + Unicode + true + Windows7.1SDK + + + DynamicLibrary + Unicode + true + Windows7.1SDK + + + DynamicLibrary + Unicode + true + Windows7.1SDK + + + DynamicLibrary + Unicode + true + Windows7.1SDK + + + DynamicLibrary + Unicode + true + Windows7.1SDK + + + DynamicLibrary + Unicode + true + Windows7.1SDK + + + DynamicLibrary + Unicode + true + Windows7.1SDK + + + DynamicLibrary + Unicode + true + Windows7.1SDK + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(SolutionDir)/bin/x86/$(Configuration)/lib/ + $(SolutionDir)/bin/x86/$(Configuration)/lib/ + $(SolutionDir)/bin/x86/$(Configuration)/lib/ + $(SolutionDir)/bin/$(Platform)/$(Configuration)/lib/ + $(SolutionDir)/bin/$(Platform)/$(Configuration)/lib/ + $(SolutionDir)/bin/$(Platform)/$(Configuration)/lib/ + $(SolutionDir)/bin/x86/$(Configuration)/lib/ + $(SolutionDir)/bin/$(Platform)/$(Configuration)/lib/ + $(SolutionDir)/bin/x86/$(Configuration)/lib/ + $(SolutionDir)/bin/$(Platform)/$(Configuration)/lib/ + $(SolutionDir)/obj/x86/$(Configuration)/$(ProjectName)/ + $(SolutionDir)/obj/x86/$(Configuration)/$(ProjectName)/ + $(SolutionDir)/obj/x86/$(Configuration)/$(ProjectName)/ + $(SolutionDir)/obj/$(Platform)/$(Configuration)/$(ProjectName)/ + $(SolutionDir)/obj/$(Platform)/$(Configuration)/$(ProjectName)/ + $(SolutionDir)/obj/$(Platform)/$(Configuration)/$(ProjectName)/ + $(SolutionDir)/obj/x86/$(Configuration)/$(ProjectName)/ + $(SolutionDir)/obj/$(Platform)/$(Configuration)/$(ProjectName)/ + $(SolutionDir)/obj/x86/$(Configuration)/$(ProjectName)/ + $(SolutionDir)/obj/$(Platform)/$(Configuration)/$(ProjectName)/ + false + false + false + false + false + false + false + false + false + false + C:\Program Files %28x86%29\PostgreSQL\9.0\include\server\port\win32_msvc;C:\Program Files %28x86%29\PostgreSQL\9.0\include\server\port\win32;C:\Program Files %28x86%29\PostgreSQL\9.0\include\server;C:\Program Files %28x86%29\PostgreSQL\9.0\include;$(IncludePath) + C:\Program Files %28x86%29\PostgreSQL\9.6\include\server\port\win32_msvc;C:\Program Files %28x86%29\PostgreSQL\9.6\include\server\port\win32;C:\Program Files %28x86%29\PostgreSQL\9.6\include\server;C:\Program Files %28x86%29\PostgreSQL\9.6\include;$(IncludePath) + C:\Program Files %28x86%29\PostgreSQL\10\include\server\port\win32_msvc;C:\Program Files %28x86%29\PostgreSQL\10\include\server\port\win32;C:\Program Files %28x86%29\PostgreSQL\10\include\server;C:\Program Files %28x86%29\PostgreSQL\10\include;$(IncludePath) + C:\Program Files\PostgreSQL\9.0\include\server\port\win32_msvc;C:\Program Files\PostgreSQL\9.0\include\server\port\win32;C:\Program Files\PostgreSQL\9.0\include\server;C:\Program Files\PostgreSQL\9.0\include;$(IncludePath) + C:\Program Files\PostgreSQL\9.6\include\server\port\win32_msvc;C:\Program Files\PostgreSQL\9.6\include\server\port\win32;C:\Program Files\PostgreSQL\9.6\include\server;C:\Program Files\PostgreSQL\9.6\include;$(IncludePath) + C:\Program Files\PostgreSQL\10\include\server\port\win32_msvc;C:\Program Files\PostgreSQL\10\include\server\port\win32;C:\Program Files\PostgreSQL\10\include\server;C:\Program Files\PostgreSQL\10\include;C:\icu\include;$(IncludePath) + C:\Program Files %28x86%29\PostgreSQL\8.4\include\server\port\win32_msvc;C:\Program Files %28x86%29\PostgreSQL\8.4\include\server\port\win32;C:\Program Files %28x86%29\PostgreSQL\8.4\include\server;C:\Program Files %28x86%29\PostgreSQL\8.4\include;$(IncludePath) + C:\Program Files %28x86%29\PostgreSQL\8.4\include\server\port\win32_msvc;C:\Program Files %28x86%29\PostgreSQL\8.4\include\server\port\win32;C:\Program Files %28x86%29\PostgreSQL\8.4\include\server;C:\Program Files %28x86%29\PostgreSQL\8.4\include;$(IncludePath) + C:\Program Files %28x86%29\PostgreSQL\8.3\include\server\port\win32_msvc;C:\Program Files %28x86%29\PostgreSQL\8.3\include\server\port\win32;C:\Program Files %28x86%29\PostgreSQL\8.3\include\server;C:\Program Files %28x86%29\PostgreSQL\8.3\include;$(IncludePath) + C:\Program Files %28x86%29\PostgreSQL\8.3\include\server\port\win32_msvc;C:\Program Files %28x86%29\PostgreSQL\8.3\include\server\port\win32;C:\Program Files %28x86%29\PostgreSQL\8.3\include\server;C:\Program Files %28x86%29\PostgreSQL\8.3\include;$(IncludePath) + C:\Program Files %28x86%29\PostgreSQL\9.0\lib;$(LibraryPath) + C:\Program Files %28x86%29\PostgreSQL\9.6\lib;$(LibraryPath) + C:\Program Files %28x86%29\PostgreSQL\10\lib;$(LibraryPath) + C:\Program Files\PostgreSQL\9.0\lib;$(LibraryPath) + C:\Program Files\PostgreSQL\9.6\lib;$(LibraryPath) + C:\icu\lib64;C:\Program Files\PostgreSQL\10\lib;$(LibraryPath) + C:\Program Files %28x86%29\PostgreSQL\8.4\lib;$(LibraryPath) + C:\Program Files %28x86%29\PostgreSQL\8.4\lib;$(LibraryPath) + C:\Program Files %28x86%29\PostgreSQL\8.3\lib;$(LibraryPath) + C:\Program Files %28x86%29\PostgreSQL\8.3\lib;$(LibraryPath) + orafce + orafce + orafce + orafce + orafce + orafce + orafce + orafce + orafce + orafce + + + + ../include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + MultiThreadedDLL + + + Level3 + false + + + CompileAsC + 4005;4996;4018;%(DisableSpecificWarnings) + + + postgres.lib + $(OutDir)/$(TargetFileName) + false + Console + true + true + MachineX86 + + + + + ../include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + MultiThreadedDLL + + + Level3 + false + + + CompileAsC + 4005;4996;4018;%(DisableSpecificWarnings) + + + postgres.lib + $(OutDir)/$(TargetFileName) + false + Console + true + true + MachineX86 + + + + + ../include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + MultiThreadedDLL + + + Level3 + false + + + CompileAsC + 4005;4996;4018;%(DisableSpecificWarnings) + + + postgres.lib + $(OutDir)/$(TargetFileName) + false + Console + true + true + MachineX86 + + + + + ../include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + MultiThreadedDLL + + + Level3 + false + + + CompileAsC + 4005;4996;4018;%(DisableSpecificWarnings) + + + postgres.lib + $(OutDir)/$(TargetFileName) + false + Console + true + true + + + + + ../include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + MultiThreadedDLL + + + Level3 + false + + + CompileAsC + 4005;4996;4018;%(DisableSpecificWarnings) + + + postgres.lib + $(OutDir)/$(TargetFileName) + false + Console + true + true + + + + + ../include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + MultiThreadedDLL + + + Level3 + false + + + CompileAsC + 4005;4996;4018;%(DisableSpecificWarnings) + + + postgres.lib + $(OutDir)/$(TargetFileName) + false + Console + true + true + + + + + ../include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + MultiThreadedDLL + + + Level3 + false + + + CompileAsC + 4005;4996;4018;%(DisableSpecificWarnings) + + + postgres.lib + $(OutDir)/$(TargetFileName) + false + Console + true + true + MachineX86 + + + + + ../include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + MultiThreadedDLL + + + Level3 + false + + + CompileAsC + 4005;4996;4018;%(DisableSpecificWarnings) + + + postgres.lib + $(OutDir)/$(TargetFileName) + false + Console + true + true + + + + + ../include;%(AdditionalIncludeDirectories) + _USE_32BIT_TIME_T;WIN32;NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + MultiThreadedDLL + + + Level3 + false + + + CompileAsC + 4005;4996;4018;%(DisableSpecificWarnings) + + + postgres.lib + $(OutDir)/$(TargetFileName) + false + Console + true + true + MachineX86 + + + + + ../include;%(AdditionalIncludeDirectories) + _USE_32BIT_TIME_T;WIN32;NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + MultiThreadedDLL + + + Level3 + false + + + CompileAsC + 4005;4996;4018;%(DisableSpecificWarnings) + + + postgres.lib + $(OutDir)/$(TargetFileName) + false + Console + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/contrib/orafce/msvc/orafce.2010.vcxproj.filters b/contrib/orafce/msvc/orafce.2010.vcxproj.filters new file mode 100644 index 000000000..57ac0c682 --- /dev/null +++ b/contrib/orafce/msvc/orafce.2010.vcxproj.filters @@ -0,0 +1,146 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {c12fa9ac-05f6-45cb-bb38-f575a3a78f47} + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {bc5d92fe-cfa8-4220-b3cf-c63aede7db41} + + + + + src + + + src + + + regress + + + regress + + + regress + + + regress + + + regress + + + + + + + + + + + + + + + + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + + + include + + + include + + + include + + + include + + + include + + + include + + + \ No newline at end of file diff --git a/contrib/orafce/nvarchar2.c b/contrib/orafce/nvarchar2.c new file mode 100644 index 000000000..81037acdb --- /dev/null +++ b/contrib/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/contrib/orafce/orafce--3.10--3.11.sql b/contrib/orafce/orafce--3.10--3.11.sql new file mode 100644 index 000000000..767104a23 --- /dev/null +++ b/contrib/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/contrib/orafce/orafce--3.11--3.12.sql b/contrib/orafce/orafce--3.11--3.12.sql new file mode 100644 index 000000000..72675c488 --- /dev/null +++ b/contrib/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/contrib/orafce/orafce--3.12--3.13.sql b/contrib/orafce/orafce--3.12--3.13.sql new file mode 100644 index 000000000..e69de29bb diff --git a/contrib/orafce/orafce--3.13--3.14.sql b/contrib/orafce/orafce--3.13--3.14.sql new file mode 100644 index 000000000..ec8050518 --- /dev/null +++ b/contrib/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/contrib/orafce/orafce--3.14--3.15.sql b/contrib/orafce/orafce--3.14--3.15.sql new file mode 100644 index 000000000..6cf99b2af --- /dev/null +++ b/contrib/orafce/orafce--3.14--3.15.sql @@ -0,0 +1,632 @@ +-- Translate Oracle regexp modifier into PostgreSQl ones +-- Append the global modifier if $2 is true. Used internally +-- by regexp_*() functions bellow. +CREATE OR REPLACE FUNCTION oracle.translate_oracle_modifiers(text, bool) +RETURNS text +AS $$ +DECLARE + modifiers text; +BEGIN + -- Check that we don't have modifier not supported by Oracle + IF $1 ~ '[^icnsmx]' THEN + -- Modifier 's' is not supported by Oracle but it is a synonym + -- of 'n', we translate 'n' into 's' bellow. It is safe to allow it. + RAISE EXCEPTION 'argument ''flags'' has unsupported modifier(s).'; + END IF; + -- Oracle 'n' modifier correspond to 's' POSIX modifier + -- Oracle 'm' modifier correspond to 'n' POSIX modifier + modifiers := translate($1, 'nm', 'sn'); + IF $2 THEN + modifiers := modifiers || 'g'; + END IF; + RETURN modifiers; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_LIKE( string text, pattern text) -> boolean +CREATE OR REPLACE FUNCTION oracle.regexp_like(text, text) +RETURNS boolean +AS $$ + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + SELECT CASE WHEN (count(*) > 0) THEN true ELSE false END FROM regexp_matches($1, $2, 'p'); +$$ +LANGUAGE 'sql'; + +-- REGEXP_LIKE( string text, pattern text, flags text ) -> boolean +CREATE OR REPLACE FUNCTION oracle.regexp_like(text, text, text) +RETURNS boolean +AS $$ +DECLARE + modifiers text; +BEGIN + modifiers := oracle.translate_oracle_modifiers($3, false); + IF (regexp_matches($1, $2, modifiers))[1] IS NOT NULL THEN + RETURN true; + END IF; + RETURN false; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_COUNT( string text, pattern text ) -> integer +CREATE OR REPLACE FUNCTION oracle.regexp_count(text, text) +RETURNS integer +AS $$ + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + SELECT count(*)::integer FROM regexp_matches($1, $2, 'pg'); +$$ +LANGUAGE 'sql'; + +-- REGEXP_COUNT( string text, pattern text, position int ) -> integer +CREATE OR REPLACE FUNCTION oracle.regexp_count(text, text, integer) +RETURNS integer +AS $$ +DECLARE + v_cnt integer; +BEGIN + -- Check numeric arguments + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + v_cnt := (SELECT count(*)::integer FROM regexp_matches(substr($1, $3), $2, 'pg')); + RETURN v_cnt; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_COUNT( string text, pattern text, position int, flags text ) -> integer +CREATE OR REPLACE FUNCTION oracle.regexp_count(text, text, integer, text) +RETURNS integer +AS $$ +DECLARE + modifiers text; + v_cnt integer; +BEGIN + -- Check numeric arguments + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + + modifiers := oracle.translate_oracle_modifiers($4, true); + v_cnt := (SELECT count(*)::integer FROM regexp_matches(substr($1, $3), $2, modifiers)); + RETURN v_cnt; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_INSTR( string text, pattern text ) -> integer +CREATE OR REPLACE FUNCTION oracle.regexp_instr(text, text) +RETURNS integer +AS $$ +DECLARE + v_pos integer; + v_pattern text; +BEGIN + -- Without subexpression specified, assume 0 which mean that the first + -- position for the substring matching the whole pattern is returned. + -- We need to enclose the pattern between parentheses. + v_pattern := '(' || $2 || ')'; + + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + v_pos := (SELECT position((SELECT (regexp_matches($1, v_pattern, 'pg'))[1] OFFSET 0 LIMIT 1) IN $1)); + + -- position() returns NULL when not found, we need to return 0 instead + IF v_pos IS NOT NULL THEN + RETURN v_pos; + END IF; + RETURN 0; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_INSTR( string text, pattern text, position int ) -> integer +CREATE OR REPLACE FUNCTION oracle.regexp_instr(text, text, integer) +RETURNS integer +AS $$ +DECLARE + v_pos integer; + v_pattern text; +BEGIN + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + -- Without subexpression specified, assume 0 which mean that the first + -- position for the substring matching the whole pattern is returned. + -- We need to enclose the pattern between parentheses. + v_pattern := '(' || $2 || ')'; + + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + v_pos := (SELECT position((SELECT (regexp_matches(substr($1, $3), v_pattern, 'pg'))[1] OFFSET 0 LIMIT 1) IN $1)); + + -- position() returns NULL when not found, we need to return 0 instead + IF v_pos IS NOT NULL THEN + RETURN v_pos; + END IF; + RETURN 0; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_INSTR( string text, pattern text, position int, occurence int ) -> integer +CREATE OR REPLACE FUNCTION oracle.regexp_instr(text, text, integer, integer) +RETURNS integer +AS $$ +DECLARE + v_pos integer; + v_pattern text; +BEGIN + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + IF $4 < 1 THEN + RAISE EXCEPTION 'argument ''occurence'' must be a number greater than 0'; + END IF; + + -- Without subexpression specified, assume 0 which mean that the first + -- position for the substring matching the whole pattern is returned. + -- We need to enclose the pattern between parentheses. + v_pattern := '(' || $2 || ')'; + + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + v_pos := (SELECT position((SELECT (regexp_matches(substr($1, $3), v_pattern, 'pg'))[1] OFFSET $4 - 1 LIMIT 1) IN $1)); + + -- position() returns NULL when not found, we need to return 0 instead + IF v_pos IS NOT NULL THEN + RETURN v_pos; + END IF; + RETURN 0; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_INSTR( string text, pattern text, position int, occurence int, return_opt int ) -> integer +CREATE OR REPLACE FUNCTION oracle.regexp_instr(text, text, integer, integer, integer) +RETURNS integer +AS $$ +DECLARE + v_pos integer; + v_len integer; + v_pattern text; +BEGIN + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + IF $4 < 1 THEN + RAISE EXCEPTION 'argument ''occurence'' must be a number greater than 0'; + END IF; + IF $5 != 0 AND $5 != 1 THEN + RAISE EXCEPTION 'argument ''return_opt'' must be 0 or 1'; + END IF; + + -- Without subexpression specified, assume 0 which mean that the first + -- Without subexpression specified, assume 0 which mean that the first + -- position for the substring matching the whole pattern is returned. + -- We need to enclose the pattern between parentheses. + v_pattern := '(' || $2 || ')'; + + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + v_pos := (SELECT position((SELECT (regexp_matches(substr($1, $3), v_pattern, 'pg'))[1] OFFSET $4-1 LIMIT 1) IN $1)); + + -- position() returns NULL when not found, we need to return 0 instead + IF v_pos IS NOT NULL THEN + IF $5 = 1 THEN + v_len := (SELECT length((SELECT (regexp_matches(substr($1, $3), v_pattern, 'pg'))[1] OFFSET $4 - 1 LIMIT 1))); + v_pos := v_pos + v_len; + END IF; + RETURN v_pos; + END IF; + RETURN 0; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_INSTR( string text, pattern text, position int, occurence int, return_opt int, flags text ) -> integer +CREATE OR REPLACE FUNCTION oracle.regexp_instr(text, text, integer, integer, integer, text) +RETURNS integer +AS $$ +DECLARE + v_pos integer; + v_len integer; + modifiers text; + v_pattern text; +BEGIN + -- Check numeric arguments + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + IF $4 < 1 THEN + RAISE EXCEPTION 'argument ''occurence'' must be a number greater than 0'; + END IF; + IF $5 != 0 AND $5 != 1 THEN + RAISE EXCEPTION 'argument ''return_opt'' must be 0 or 1'; + END IF; + modifiers := oracle.translate_oracle_modifiers($6, true); + + -- Without subexpression specified, assume 0 which mean that the first + -- position for the substring matching the whole pattern is returned. + -- We need to enclose the pattern between parentheses. + v_pattern := '(' || $2 || ')'; + v_pos := (SELECT position((SELECT (regexp_matches(substr($1, $3), v_pattern, modifiers))[1] OFFSET $4 - 1 LIMIT 1) IN $1)); + + -- position() returns NULL when not found, we need to return 0 instead + IF v_pos IS NOT NULL THEN + IF $5 = 1 THEN + v_len := (SELECT length((SELECT (regexp_matches(substr($1, $3), v_pattern, modifiers))[1] OFFSET $4-1 LIMIT 1))); + v_pos := v_pos + v_len; + END IF; + RETURN v_pos; + END IF; + RETURN 0; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_INSTR( string text, pattern text, position int, occurence int, return_opt int, flags text, group int ) -> integer +CREATE OR REPLACE FUNCTION oracle.regexp_instr(text, text, integer, integer, integer, text, integer) +RETURNS integer +AS $$ +DECLARE + v_pos integer := 0; + v_pos_orig integer := $3; + v_len integer := 0; + modifiers text; + occurrence integer := $4; + idx integer := 1; + v_curr_pos integer := 0; + v_pattern text; + v_subexpr integer := $7; +BEGIN + -- Check numeric arguments + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + IF $4 < 1 THEN + RAISE EXCEPTION 'argument ''occurence'' must be a number greater than 0'; + END IF; + IF $7 < 0 THEN + RAISE EXCEPTION 'argument ''group'' must be a positive number'; + END IF; + IF $5 != 0 AND $5 != 1 THEN + RAISE EXCEPTION 'argument ''return_opt'' must be 0 or 1'; + END IF; + + -- Translate Oracle regexp modifier into PostgreSQl ones + modifiers := oracle.translate_oracle_modifiers($6, true); + + -- If subexpression value is 0 we need to enclose the pattern between parentheses. + IF v_subexpr = 0 THEN + v_pattern := '(' || $2 || ')'; + v_subexpr := 1; + ELSE + v_pattern := $2; + END IF; + + -- To get position of occurrence > 1 we need a more complex code + LOOP + v_curr_pos := v_curr_pos + v_len; + v_pos := (SELECT position((SELECT (regexp_matches(substr($1, v_pos_orig), '('||$2||')', modifiers))[1] OFFSET 0 LIMIT 1) IN substr($1, v_pos_orig))); + v_len := (SELECT length((SELECT (regexp_matches(substr($1, v_pos_orig), '('||$2||')', modifiers))[1] OFFSET 0 LIMIT 1))); + + EXIT WHEN v_len IS NULL; + + v_pos_orig := v_pos_orig + v_pos + v_len; + v_curr_pos := v_curr_pos + v_pos; + idx := idx + 1; + + EXIT WHEN idx > occurrence; + END LOOP; + + v_pos := (SELECT position((SELECT (regexp_matches(substr($1, v_curr_pos), v_pattern, modifiers))[v_subexpr] OFFSET 0 LIMIT 1) IN substr($1, v_curr_pos))); + IF v_pos IS NOT NULL THEN + IF $5 = 1 THEN + v_len := (SELECT length((SELECT (regexp_matches(substr($1, v_curr_pos), v_pattern, modifiers))[v_subexpr] OFFSET 0 LIMIT 1))); + v_pos := v_pos + v_len; + END IF; + RETURN v_pos + v_curr_pos - 1; + END IF; + RETURN 0; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_SUBSTR( string text, pattern text ) -> text +CREATE OR REPLACE FUNCTION oracle.regexp_substr(text, text) +RETURNS text +AS $$ +DECLARE + v_substr text; + v_pattern text; +BEGIN + -- Without subexpression specified, assume 0 which mean that the first + -- position for the substring matching the whole pattern is returned. + -- We need to enclose the pattern between parentheses. + v_pattern := '(' || $2 || ')'; + + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + v_substr := (SELECT (regexp_matches($1, v_pattern, 'pg'))[1] OFFSET 0 LIMIT 1); + RETURN v_substr; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_SUBSTR( string text, pattern text, position int ) -> text +CREATE OR REPLACE FUNCTION oracle.regexp_substr(text, text, int) +RETURNS text +AS $$ +DECLARE + v_substr text; + v_pattern text; +BEGIN + -- Check numeric arguments + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + + -- Without subexpression specified, assume 0 which mean that the first + -- position for the substring matching the whole pattern is returned. + -- We need to enclose the pattern between parentheses. + v_pattern := '(' || $2 || ')'; + + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + v_substr := (SELECT (regexp_matches(substr($1, $3), v_pattern, 'pg'))[1] OFFSET 0 LIMIT 1); + RETURN v_substr; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_SUBSTR( string text, pattern text, position int, occurence int ) -> text +CREATE OR REPLACE FUNCTION oracle.regexp_substr(text, text, integer, integer) +RETURNS text +AS $$ +DECLARE + v_substr text; + v_pattern text; +BEGIN + -- Check numeric arguments + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + IF $4 < 1 THEN + RAISE EXCEPTION 'argument ''occurence'' must be a number greater than 0'; + END IF; + + -- Without subexpression specified, assume 0 which mean that the first + -- position for the substring matching the whole pattern is returned. + -- We need to enclose the pattern between parentheses. + v_pattern := '(' || $2 || ')'; + + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + v_substr := (SELECT (regexp_matches(substr($1, $3), v_pattern, 'pg'))[1] OFFSET $4 - 1 LIMIT 1); + RETURN v_substr; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_SUBSTR( string text, pattern text, position int, occurence int, flags text ) -> text +CREATE OR REPLACE FUNCTION oracle.regexp_substr(text, text, integer, integer, text) +RETURNS text +AS $$ +DECLARE + v_substr text; + v_pattern text; + modifiers text; +BEGIN + -- Check numeric arguments + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + IF $4 < 1 THEN + RAISE EXCEPTION 'argument ''occurence'' must be a number greater than 0'; + END IF; + + modifiers := oracle.translate_oracle_modifiers($5, true); + + -- Without subexpression specified, assume 0 which mean that the first + -- position for the substring matching the whole pattern is returned. + -- We need to enclose the pattern between parentheses. + v_pattern := '(' || $2 || ')'; + + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + v_substr := (SELECT (regexp_matches(substr($1, $3), v_pattern, modifiers))[1] OFFSET $4 - 1 LIMIT 1); + RETURN v_substr; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_SUBSTR( string text, pattern text, position int, occurence int, flags text, group int ) -> text +CREATE OR REPLACE FUNCTION oracle.regexp_substr(text, text, integer, integer, text, int) +RETURNS text +AS $$ +DECLARE + v_substr text; + v_pattern text; + modifiers text; + v_subexpr integer := $6; + has_group integer; +BEGIN + -- Check numeric arguments + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + IF $4 < 1 THEN + RAISE EXCEPTION 'argument ''occurence'' must be a number greater than 0'; + END IF; + IF v_subexpr < 0 THEN + RAISE EXCEPTION 'argument ''group'' must be a positive number'; + END IF; + + -- Check that with v_subexpr = 1 we have a capture group otherwise return NULL + has_group := (SELECT count(*) FROM regexp_matches(v_pattern, '\(.*\)')); + IF $6 = 1 AND has_group = 0 THEN + RETURN NULL; + END IF; + + modifiers := oracle.translate_oracle_modifiers($5, true); + + -- If subexpression value is 0 we need to enclose the pattern between parentheses. + IF v_subexpr = 0 THEN + v_pattern := '(' || $2 || ')'; + v_subexpr := 1; + ELSE + v_pattern := $2; + END IF; + + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + v_substr := (SELECT (regexp_matches(substr($1, $3), v_pattern, modifiers))[v_subexpr] OFFSET $4 - 1 LIMIT 1); + RETURN v_substr; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_REPLACE( string text, pattern text, replace_string text ) -> text +CREATE OR REPLACE FUNCTION oracle.regexp_replace(text, text, text) +RETURNS text +AS $$ + -- Oracle default behavior is to replace all occurence + -- whereas PostgreSQL only replace the first occurrence + -- so we need to add 'g' modifier. + SELECT pg_catalog.regexp_replace($1, $2, $3, 'g'); +$$ +LANGUAGE sql; + +-- REGEXP_REPLACE( string text, pattern text, replace_string text, position int ) -> text +CREATE OR REPLACE FUNCTION oracle.regexp_replace(text, text, text, integer) +RETURNS text +AS $$ +DECLARE + v_replaced_str text; + v_before text; +BEGIN + -- Check numeric arguments + IF $4 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + + v_before = substr($1, 1, $4 - 1); + + -- Oracle default behavior is to replace all occurence + -- whereas PostgreSQL only replace the first occurrence + -- so we need to add 'g' modifier. + v_replaced_str := v_before || pg_catalog.regexp_replace(substr($1, $4), $2, $3, 'g'); + RETURN v_replaced_str; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_REPLACE( string text, pattern text, replace_string text, position int, occurence int ) -> text +CREATE OR REPLACE FUNCTION oracle.regexp_replace(text, text, text, integer, integer) +RETURNS text +AS $$ +DECLARE + v_replaced_str text; + v_pos integer := $4; + v_before text := ''; + v_nummatch integer; +BEGIN + -- Check numeric arguments + IF $4 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + IF $5 < 0 THEN + RAISE EXCEPTION 'argument ''occurrence'' must be a positive number'; + END IF; + -- Check if the occurrence queried exceeds the number of occurrences + IF $5 > 1 THEN + v_nummatch := (SELECT count(*) FROM regexp_matches(substr($1, $4), $2, 'g')); + IF $5 > v_nummatch THEN + RETURN $1; + END IF; + -- Get the position of the occurrence we are looking for + v_pos := oracle.regexp_instr($1, $2, $4, $5, 0, '', 1); + IF v_pos = 0 THEN + RETURN $1; + END IF; + END IF; + -- Get the substring before this position we will need to restore it + v_before := substr($1, 1, v_pos - 1); + + -- Replace all occurrences + IF $5 = 0 THEN + v_replaced_str := v_before || pg_catalog.regexp_replace(substr($1, v_pos), $2, $3, 'g'); + ELSE + -- Replace the first occurrence + v_replaced_str := v_before || pg_catalog.regexp_replace(substr($1, v_pos), $2, $3); + END IF; + + RETURN v_replaced_str; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_REPLACE( string text, pattern text, replace_string text, position int, occurence int, flags text ) -> text +CREATE OR REPLACE FUNCTION oracle.regexp_replace(text, text, text, integer, integer, text) +RETURNS text +AS $$ +DECLARE + v_replaced_str text; + v_pos integer := $4; + v_nummatch integer; + v_before text := ''; + modifiers text := ''; +BEGIN + -- Check numeric arguments + IF $4 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + IF $5 < 0 THEN + RAISE EXCEPTION 'argument ''occurrence'' must be a positive number'; + END IF; + -- Set the modifiers + IF $5 = 0 THEN + modifiers := oracle.translate_oracle_modifiers($6, true); + ELSE + modifiers := oracle.translate_oracle_modifiers($6, false); + END IF; + -- Check if the occurrence queried exceeds the number of occurrences + IF $5 > 1 THEN + v_nummatch := (SELECT count(*) FROM regexp_matches(substr($1, $4), $2, $6||'g')); + IF $5 > v_nummatch THEN + RETURN $1; + END IF; + -- Get the position of the occurrence we are looking for + v_pos := oracle.regexp_instr($1, $2, $4, $5, 0, $6, 1); + IF v_pos = 0 THEN + RETURN $1; + END IF; + END IF; + -- Get the substring before this position we will need to restore it + v_before := substr($1, 1, v_pos - 1); + -- Replace occurrence(s) + v_replaced_str := v_before || pg_catalog.regexp_replace(substr($1, v_pos), $2, $3, modifiers); + RETURN v_replaced_str; +END; +$$ +LANGUAGE plpgsql; + diff --git a/contrib/orafce/orafce--3.15--3.16.sql b/contrib/orafce/orafce--3.15--3.16.sql new file mode 100644 index 000000000..bee468028 --- /dev/null +++ b/contrib/orafce/orafce--3.15--3.16.sql @@ -0,0 +1,5 @@ +CREATE FUNCTION dbms_utility.get_time() +RETURNS int +AS 'MODULE_PATHNAME','dbms_utility_get_time' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION dbms_utility.get_time() IS 'Returns the number of hundredths of seconds that have elapsed since point in time'; diff --git a/contrib/orafce/orafce--3.16--3.17.sql b/contrib/orafce/orafce--3.16--3.17.sql new file mode 100644 index 000000000..bae0c37a9 --- /dev/null +++ b/contrib/orafce/orafce--3.16--3.17.sql @@ -0,0 +1,685 @@ +-- Translate Oracle regexp modifier into PostgreSQl ones +-- Append the global modifier if $2 is true. Used internally +-- by regexp_*() functions bellow. +CREATE OR REPLACE FUNCTION oracle.translate_oracle_modifiers(text, bool) +RETURNS text +AS $$ +DECLARE + modifiers text := ''; +BEGIN + IF $1 IS NOT NULL THEN + -- Check that we don't have modifier not supported by Oracle + IF $1 ~ '[^icnsmx]' THEN + -- Modifier 's' is not supported by Oracle but it is a synonym + -- of 'n', we translate 'n' into 's' bellow. It is safe to allow it. + RAISE EXCEPTION 'argument ''flags'' has unsupported modifier(s).'; + END IF; + -- Oracle 'n' modifier correspond to 's' POSIX modifier + -- Oracle 'm' modifier correspond to 'n' POSIX modifier + modifiers := translate($1, 'nm', 'sn'); + END IF; + IF $2 THEN + modifiers := modifiers || 'g'; + END IF; + RETURN modifiers; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_LIKE( string text, pattern text) -> boolean +-- If one of the param is NULL returns NULL, declared STRICT +CREATE OR REPLACE FUNCTION oracle.regexp_like(text, text) +RETURNS boolean +AS $$ + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + SELECT CASE WHEN (count(*) > 0) THEN true ELSE false END FROM regexp_matches($1, $2, 'p'); +$$ +LANGUAGE 'sql' STRICT; + +-- REGEXP_LIKE( string text, pattern text, flags text ) -> boolean +CREATE OR REPLACE FUNCTION oracle.regexp_like(text, text, text) +RETURNS boolean +AS $$ +DECLARE + modifiers text; +BEGIN + -- Only modifier can be NULL + IF $1 IS NULL OR $2 IS NULL THEN + RETURN NULL; + END IF; + modifiers := oracle.translate_oracle_modifiers($3, false); + IF (regexp_matches($1, $2, modifiers))[1] IS NOT NULL THEN + RETURN true; + END IF; + RETURN false; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_COUNT( string text, pattern text ) -> integer +CREATE OR REPLACE FUNCTION oracle.regexp_count(text, text) +RETURNS integer +AS $$ + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + SELECT count(*)::integer FROM regexp_matches($1, $2, 'pg'); +$$ +LANGUAGE 'sql' STRICT; + +-- REGEXP_COUNT( string text, pattern text, position int ) -> integer +CREATE OR REPLACE FUNCTION oracle.regexp_count(text, text, integer) +RETURNS integer +AS $$ +DECLARE + v_cnt integer; +BEGIN + -- Check numeric arguments + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + v_cnt := (SELECT count(*)::integer FROM regexp_matches(substr($1, $3), $2, 'pg')); + RETURN v_cnt; +END; +$$ +LANGUAGE plpgsql STRICT; + +-- REGEXP_COUNT( string text, pattern text, position int, flags text ) -> integer +CREATE OR REPLACE FUNCTION oracle.regexp_count(text, text, integer, text) +RETURNS integer +AS $$ +DECLARE + modifiers text; + v_cnt integer; +BEGIN + -- Only modifier can be NULL + IF $1 IS NULL OR $2 IS NULL OR $3 IS NULL THEN + RETURN NULL; + END IF; + -- Check numeric arguments + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + + modifiers := oracle.translate_oracle_modifiers($4, true); + v_cnt := (SELECT count(*)::integer FROM regexp_matches(substr($1, $3), $2, modifiers)); + RETURN v_cnt; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_INSTR( string text, pattern text ) -> integer +CREATE OR REPLACE FUNCTION oracle.regexp_instr(text, text) +RETURNS integer +AS $$ +DECLARE + v_pos integer; + v_pattern text; +BEGIN + -- Without subexpression specified, assume 0 which mean that the first + -- position for the substring matching the whole pattern is returned. + -- We need to enclose the pattern between parentheses. + v_pattern := '(' || $2 || ')'; + + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + v_pos := (SELECT position((SELECT (regexp_matches($1, v_pattern, 'pg'))[1] OFFSET 0 LIMIT 1) IN $1)); + + -- position() returns NULL when not found, we need to return 0 instead + IF v_pos IS NOT NULL THEN + RETURN v_pos; + END IF; + RETURN 0; +END; +$$ +LANGUAGE plpgsql STRICT; + +-- REGEXP_INSTR( string text, pattern text, position int ) -> integer +CREATE OR REPLACE FUNCTION oracle.regexp_instr(text, text, integer) +RETURNS integer +AS $$ +DECLARE + v_pos integer; + v_pattern text; +BEGIN + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + -- Without subexpression specified, assume 0 which mean that the first + -- position for the substring matching the whole pattern is returned. + -- We need to enclose the pattern between parentheses. + v_pattern := '(' || $2 || ')'; + + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + v_pos := (SELECT position((SELECT (regexp_matches(substr($1, $3), v_pattern, 'pg'))[1] OFFSET 0 LIMIT 1) IN $1)); + + -- position() returns NULL when not found, we need to return 0 instead + IF v_pos IS NOT NULL THEN + RETURN v_pos; + END IF; + RETURN 0; +END; +$$ +LANGUAGE plpgsql STRICT; + +-- REGEXP_INSTR( string text, pattern text, position int, occurence int ) -> integer +CREATE OR REPLACE FUNCTION oracle.regexp_instr(text, text, integer, integer) +RETURNS integer +AS $$ +DECLARE + v_pos integer; + v_pattern text; +BEGIN + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + IF $4 < 1 THEN + RAISE EXCEPTION 'argument ''occurence'' must be a number greater than 0'; + END IF; + + -- Without subexpression specified, assume 0 which mean that the first + -- position for the substring matching the whole pattern is returned. + -- We need to enclose the pattern between parentheses. + v_pattern := '(' || $2 || ')'; + + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + v_pos := (SELECT position((SELECT (regexp_matches(substr($1, $3), v_pattern, 'pg'))[1] OFFSET $4 - 1 LIMIT 1) IN $1)); + + -- position() returns NULL when not found, we need to return 0 instead + IF v_pos IS NOT NULL THEN + RETURN v_pos; + END IF; + RETURN 0; +END; +$$ +LANGUAGE plpgsql STRICT; + +-- REGEXP_INSTR( string text, pattern text, position int, occurence int, return_opt int ) -> integer +CREATE OR REPLACE FUNCTION oracle.regexp_instr(text, text, integer, integer, integer) +RETURNS integer +AS $$ +DECLARE + v_pos integer; + v_len integer; + v_pattern text; +BEGIN + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + IF $4 < 1 THEN + RAISE EXCEPTION 'argument ''occurence'' must be a number greater than 0'; + END IF; + IF $5 != 0 AND $5 != 1 THEN + RAISE EXCEPTION 'argument ''return_opt'' must be 0 or 1'; + END IF; + + -- Without subexpression specified, assume 0 which mean that the first + -- Without subexpression specified, assume 0 which mean that the first + -- position for the substring matching the whole pattern is returned. + -- We need to enclose the pattern between parentheses. + v_pattern := '(' || $2 || ')'; + + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + v_pos := (SELECT position((SELECT (regexp_matches(substr($1, $3), v_pattern, 'pg'))[1] OFFSET $4-1 LIMIT 1) IN $1)); + + -- position() returns NULL when not found, we need to return 0 instead + IF v_pos IS NOT NULL THEN + IF $5 = 1 THEN + v_len := (SELECT length((SELECT (regexp_matches(substr($1, $3), v_pattern, 'pg'))[1] OFFSET $4 - 1 LIMIT 1))); + v_pos := v_pos + v_len; + END IF; + RETURN v_pos; + END IF; + RETURN 0; +END; +$$ +LANGUAGE plpgsql STRICT; + +-- REGEXP_INSTR( string text, pattern text, position int, occurence int, return_opt int, flags text ) -> integer +CREATE OR REPLACE FUNCTION oracle.regexp_instr(text, text, integer, integer, integer, text) +RETURNS integer +AS $$ +DECLARE + v_pos integer; + v_len integer; + modifiers text; + v_pattern text; +BEGIN + -- Only modifier can be NULL + IF $1 IS NULL OR $2 IS NULL OR $3 IS NULL OR $4 IS NULL OR $5 IS NULL THEN + RETURN NULL; + END IF; + -- Check numeric arguments + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + IF $4 < 1 THEN + RAISE EXCEPTION 'argument ''occurence'' must be a number greater than 0'; + END IF; + IF $5 != 0 AND $5 != 1 THEN + RAISE EXCEPTION 'argument ''return_opt'' must be 0 or 1'; + END IF; + modifiers := oracle.translate_oracle_modifiers($6, true); + + -- Without subexpression specified, assume 0 which mean that the first + -- position for the substring matching the whole pattern is returned. + -- We need to enclose the pattern between parentheses. + v_pattern := '(' || $2 || ')'; + v_pos := (SELECT position((SELECT (regexp_matches(substr($1, $3), v_pattern, modifiers))[1] OFFSET $4 - 1 LIMIT 1) IN $1)); + + -- position() returns NULL when not found, we need to return 0 instead + IF v_pos IS NOT NULL THEN + IF $5 = 1 THEN + v_len := (SELECT length((SELECT (regexp_matches(substr($1, $3), v_pattern, modifiers))[1] OFFSET $4-1 LIMIT 1))); + v_pos := v_pos + v_len; + END IF; + RETURN v_pos; + END IF; + RETURN 0; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_INSTR( string text, pattern text, position int, occurence int, return_opt int, flags text, group int ) -> integer +CREATE OR REPLACE FUNCTION oracle.regexp_instr(text, text, integer, integer, integer, text, integer) +RETURNS integer +AS $$ +DECLARE + v_pos integer := 0; + v_pos_orig integer := $3; + v_len integer := 0; + modifiers text; + occurrence integer := $4; + idx integer := 1; + v_curr_pos integer := 0; + v_pattern text; + v_subexpr integer := $7; +BEGIN + -- Only modifier can be NULL + IF $1 IS NULL OR $2 IS NULL OR $3 IS NULL OR $4 IS NULL OR $5 IS NULL OR $7 IS NULL THEN + RETURN NULL; + END IF; + -- Check numeric arguments + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + IF $4 < 1 THEN + RAISE EXCEPTION 'argument ''occurence'' must be a number greater than 0'; + END IF; + IF $7 < 0 THEN + RAISE EXCEPTION 'argument ''group'' must be a positive number'; + END IF; + IF $5 != 0 AND $5 != 1 THEN + RAISE EXCEPTION 'argument ''return_opt'' must be 0 or 1'; + END IF; + + -- Translate Oracle regexp modifier into PostgreSQl ones + modifiers := oracle.translate_oracle_modifiers($6, true); + + -- If subexpression value is 0 we need to enclose the pattern between parentheses. + IF v_subexpr = 0 THEN + v_pattern := '(' || $2 || ')'; + v_subexpr := 1; + ELSE + v_pattern := $2; + END IF; + + -- To get position of occurrence > 1 we need a more complex code + LOOP + v_curr_pos := v_curr_pos + v_len; + v_pos := (SELECT position((SELECT (regexp_matches(substr($1, v_pos_orig), '('||$2||')', modifiers))[1] OFFSET 0 LIMIT 1) IN substr($1, v_pos_orig))); + v_len := (SELECT length((SELECT (regexp_matches(substr($1, v_pos_orig), '('||$2||')', modifiers))[1] OFFSET 0 LIMIT 1))); + + EXIT WHEN v_len IS NULL; + + v_pos_orig := v_pos_orig + v_pos + v_len; + v_curr_pos := v_curr_pos + v_pos; + idx := idx + 1; + + EXIT WHEN idx > occurrence; + END LOOP; + + v_pos := (SELECT position((SELECT (regexp_matches(substr($1, v_curr_pos), v_pattern, modifiers))[v_subexpr] OFFSET 0 LIMIT 1) IN substr($1, v_curr_pos))); + IF v_pos IS NOT NULL THEN + IF $5 = 1 THEN + v_len := (SELECT length((SELECT (regexp_matches(substr($1, v_curr_pos), v_pattern, modifiers))[v_subexpr] OFFSET 0 LIMIT 1))); + v_pos := v_pos + v_len; + END IF; + RETURN v_pos + v_curr_pos - 1; + END IF; + RETURN 0; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_SUBSTR( string text, pattern text ) -> text +CREATE OR REPLACE FUNCTION oracle.regexp_substr(text, text) +RETURNS text +AS $$ +DECLARE + v_substr text; + v_pattern text; +BEGIN + -- Without subexpression specified, assume 0 which mean that the first + -- position for the substring matching the whole pattern is returned. + -- We need to enclose the pattern between parentheses. + v_pattern := '(' || $2 || ')'; + + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + v_substr := (SELECT (regexp_matches($1, v_pattern, 'pg'))[1] OFFSET 0 LIMIT 1); + RETURN v_substr; +END; +$$ +LANGUAGE plpgsql STRICT; + +-- REGEXP_SUBSTR( string text, pattern text, position int ) -> text +CREATE OR REPLACE FUNCTION oracle.regexp_substr(text, text, int) +RETURNS text +AS $$ +DECLARE + v_substr text; + v_pattern text; +BEGIN + -- Check numeric arguments + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + + -- Without subexpression specified, assume 0 which mean that the first + -- position for the substring matching the whole pattern is returned. + -- We need to enclose the pattern between parentheses. + v_pattern := '(' || $2 || ')'; + + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + v_substr := (SELECT (regexp_matches(substr($1, $3), v_pattern, 'pg'))[1] OFFSET 0 LIMIT 1); + RETURN v_substr; +END; +$$ +LANGUAGE plpgsql STRICT; + +-- REGEXP_SUBSTR( string text, pattern text, position int, occurence int ) -> text +CREATE OR REPLACE FUNCTION oracle.regexp_substr(text, text, integer, integer) +RETURNS text +AS $$ +DECLARE + v_substr text; + v_pattern text; +BEGIN + -- Check numeric arguments + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + IF $4 < 1 THEN + RAISE EXCEPTION 'argument ''occurence'' must be a number greater than 0'; + END IF; + + -- Without subexpression specified, assume 0 which mean that the first + -- position for the substring matching the whole pattern is returned. + -- We need to enclose the pattern between parentheses. + v_pattern := '(' || $2 || ')'; + + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + v_substr := (SELECT (regexp_matches(substr($1, $3), v_pattern, 'pg'))[1] OFFSET $4 - 1 LIMIT 1); + RETURN v_substr; +END; +$$ +LANGUAGE plpgsql STRICT; + +-- REGEXP_SUBSTR( string text, pattern text, position int, occurence int, flags text ) -> text +CREATE OR REPLACE FUNCTION oracle.regexp_substr(text, text, integer, integer, text) +RETURNS text +AS $$ +DECLARE + v_substr text; + v_pattern text; + modifiers text; +BEGIN + -- Only modifier can be NULL + IF $1 IS NULL OR $2 IS NULL OR $3 IS NULL OR $4 IS NULL THEN + RETURN NULL; + END IF; + -- Check numeric arguments + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + IF $4 < 1 THEN + RAISE EXCEPTION 'argument ''occurence'' must be a number greater than 0'; + END IF; + + modifiers := oracle.translate_oracle_modifiers($5, true); + + -- Without subexpression specified, assume 0 which mean that the first + -- position for the substring matching the whole pattern is returned. + -- We need to enclose the pattern between parentheses. + v_pattern := '(' || $2 || ')'; + + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + v_substr := (SELECT (regexp_matches(substr($1, $3), v_pattern, modifiers))[1] OFFSET $4 - 1 LIMIT 1); + RETURN v_substr; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_SUBSTR( string text, pattern text, position int, occurence int, flags text, group int ) -> text +CREATE OR REPLACE FUNCTION oracle.regexp_substr(text, text, integer, integer, text, int) +RETURNS text +AS $$ +DECLARE + v_substr text; + v_pattern text; + modifiers text; + v_subexpr integer := $6; + has_group integer; +BEGIN + -- Only modifier can be NULL + IF $1 IS NULL OR $2 IS NULL OR $3 IS NULL OR $4 IS NULL OR $6 IS NULL THEN + RETURN NULL; + END IF; + -- Check numeric arguments + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + IF $4 < 1 THEN + RAISE EXCEPTION 'argument ''occurence'' must be a number greater than 0'; + END IF; + IF v_subexpr < 0 THEN + RAISE EXCEPTION 'argument ''group'' must be a positive number'; + END IF; + + -- Check that with v_subexpr = 1 we have a capture group otherwise return NULL + has_group := (SELECT count(*) FROM regexp_matches($2, '(?:[^\\]|^)\(', 'g')); + IF $6 = 1 AND has_group = 0 THEN + RETURN NULL; + END IF; + + modifiers := oracle.translate_oracle_modifiers($5, true); + + -- If subexpression value is 0 we need to enclose the pattern between parentheses. + IF v_subexpr = 0 THEN + v_pattern := '(' || $2 || ')'; + v_subexpr := 1; + ELSE + v_pattern := $2; + END IF; + + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + v_substr := (SELECT (regexp_matches(substr($1, $3), v_pattern, modifiers))[v_subexpr] OFFSET $4 - 1 LIMIT 1); + RETURN v_substr; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_REPLACE( string text, pattern text, replace_string text ) -> text +CREATE OR REPLACE FUNCTION oracle.regexp_replace(text, text, text) +RETURNS text +AS $$ +DECLARE + str text; +BEGIN + IF $2 IS NULL AND $1 IS NOT NULL THEN + RETURN $1; + END IF; + -- Oracle default behavior is to replace all occurence + -- whereas PostgreSQL only replace the first occurrence + -- so we need to add 'g' modifier. + SELECT pg_catalog.regexp_replace($1, $2, $3, 'g') INTO str; + RETURN str; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_REPLACE( string text, pattern text, replace_string text, position int ) -> text +CREATE OR REPLACE FUNCTION oracle.regexp_replace(text, text, text, integer) +RETURNS text +AS $$ +DECLARE + v_replaced_str text; + v_before text; +BEGIN + IF $1 IS NULL OR $3 IS NULL OR $4 IS NULL THEN + RETURN NULL; + END IF; + IF $2 IS NULL THEN + RETURN $1; + END IF; + -- Check numeric arguments + IF $4 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + + v_before = substr($1, 1, $4 - 1); + + -- Oracle default behavior is to replace all occurence + -- whereas PostgreSQL only replace the first occurrence + -- so we need to add 'g' modifier. + v_replaced_str := v_before || pg_catalog.regexp_replace(substr($1, $4), $2, $3, 'g'); + RETURN v_replaced_str; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_REPLACE( string text, pattern text, replace_string text, position int, occurence int ) -> text +CREATE OR REPLACE FUNCTION oracle.regexp_replace(text, text, text, integer, integer) +RETURNS text +AS $$ +DECLARE + v_replaced_str text; + v_pos integer := $4; + v_before text := ''; + v_nummatch integer; +BEGIN + IF $1 IS NULL OR $3 IS NULL OR $4 IS NULL OR $5 IS NULL THEN + RETURN NULL; + END IF; + IF $2 IS NULL THEN + RETURN $1; + END IF; + -- Check numeric arguments + IF $4 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + IF $5 < 0 THEN + RAISE EXCEPTION 'argument ''occurrence'' must be a positive number'; + END IF; + -- Check if the occurrence queried exceeds the number of occurrences + IF $5 > 1 THEN + v_nummatch := (SELECT count(*) FROM regexp_matches(substr($1, $4), $2, 'g')); + IF $5 > v_nummatch THEN + RETURN $1; + END IF; + -- Get the position of the occurrence we are looking for + v_pos := oracle.regexp_instr($1, $2, $4, $5, 0, '', 1); + IF v_pos = 0 THEN + RETURN $1; + END IF; + END IF; + -- Get the substring before this position we will need to restore it + v_before := substr($1, 1, v_pos - 1); + + -- Replace all occurrences + IF $5 = 0 THEN + v_replaced_str := v_before || pg_catalog.regexp_replace(substr($1, v_pos), $2, $3, 'g'); + ELSE + -- Replace the first occurrence + v_replaced_str := v_before || pg_catalog.regexp_replace(substr($1, v_pos), $2, $3); + END IF; + + RETURN v_replaced_str; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_REPLACE( string text, pattern text, replace_string text, position int, occurence int, flags text ) -> text +CREATE OR REPLACE FUNCTION oracle.regexp_replace(text, text, text, integer, integer, text) +RETURNS text +AS $$ +DECLARE + v_replaced_str text; + v_pos integer := $4; + v_nummatch integer; + v_before text := ''; + modifiers text := ''; +BEGIN + IF $1 IS NULL OR $3 IS NULL OR $4 IS NULL OR $5 IS NULL THEN + RETURN NULL; + END IF; + IF $2 IS NULL THEN + RETURN $1; + END IF; + -- Check numeric arguments + IF $4 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + IF $5 < 0 THEN + RAISE EXCEPTION 'argument ''occurrence'' must be a positive number'; + END IF; + -- Set the modifiers + IF $5 = 0 THEN + modifiers := oracle.translate_oracle_modifiers($6, true); + ELSE + modifiers := oracle.translate_oracle_modifiers($6, false); + END IF; + -- Check if the occurrence queried exceeds the number of occurrences + IF $5 > 1 THEN + v_nummatch := (SELECT count(*) FROM regexp_matches(substr($1, $4), $2, $6||'g')); + IF $5 > v_nummatch THEN + RETURN $1; + END IF; + -- Get the position of the occurrence we are looking for + v_pos := oracle.regexp_instr($1, $2, $4, $5, 0, $6, 1); + IF v_pos = 0 THEN + RETURN $1; + END IF; + END IF; + -- Get the substring before this position we will need to restore it + v_before := substr($1, 1, v_pos - 1); + -- Replace occurrence(s) + v_replaced_str := v_before || pg_catalog.regexp_replace(substr($1, v_pos), $2, $3, modifiers); + RETURN v_replaced_str; +END; +$$ +LANGUAGE plpgsql; + diff --git a/contrib/orafce/orafce--3.17.sql b/contrib/orafce/orafce--3.17.sql new file mode 100644 index 000000000..b6bb97510 --- /dev/null +++ b/contrib/orafce/orafce--3.17.sql @@ -0,0 +1,4244 @@ +/* orafce--3.17.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 FUNCTION dbms_utility.get_time() +RETURNS int +AS 'MODULE_PATHNAME','dbms_utility_get_time' +LANGUAGE C VOLATILE; +COMMENT ON FUNCTION dbms_utility.get_time() IS 'Returns the number of hundredths of seconds that have elapsed since point in time'; + +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 + 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 +$$; + +-- 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 + 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 +$$; + +/* + * 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'; + +-- Translate Oracle regexp modifier into PostgreSQl ones +-- Append the global modifier if $2 is true. Used internally +-- by regexp_*() functions bellow. +CREATE OR REPLACE FUNCTION oracle.translate_oracle_modifiers(text, bool) +RETURNS text +AS $$ +DECLARE + modifiers text := ''; +BEGIN + IF $1 IS NOT NULL THEN + -- Check that we don't have modifier not supported by Oracle + IF $1 ~ '[^icnsmx]' THEN + -- Modifier 's' is not supported by Oracle but it is a synonym + -- of 'n', we translate 'n' into 's' bellow. It is safe to allow it. + RAISE EXCEPTION 'argument ''flags'' has unsupported modifier(s).'; + END IF; + -- Oracle 'n' modifier correspond to 's' POSIX modifier + -- Oracle 'm' modifier correspond to 'n' POSIX modifier + modifiers := translate($1, 'nm', 'sn'); + END IF; + IF $2 THEN + modifiers := modifiers || 'g'; + END IF; + RETURN modifiers; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_LIKE( string text, pattern text) -> boolean +-- If one of the param is NULL returns NULL, declared STRICT +CREATE OR REPLACE FUNCTION oracle.regexp_like(text, text) +RETURNS boolean +AS $$ + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + SELECT CASE WHEN (count(*) > 0) THEN true ELSE false END FROM regexp_matches($1, $2, 'p'); +$$ +LANGUAGE 'sql' STRICT; + +-- REGEXP_LIKE( string text, pattern text, flags text ) -> boolean +CREATE OR REPLACE FUNCTION oracle.regexp_like(text, text, text) +RETURNS boolean +AS $$ +DECLARE + modifiers text; +BEGIN + -- Only modifier can be NULL + IF $1 IS NULL OR $2 IS NULL THEN + RETURN NULL; + END IF; + modifiers := oracle.translate_oracle_modifiers($3, false); + IF (regexp_matches($1, $2, modifiers))[1] IS NOT NULL THEN + RETURN true; + END IF; + RETURN false; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_COUNT( string text, pattern text ) -> integer +CREATE OR REPLACE FUNCTION oracle.regexp_count(text, text) +RETURNS integer +AS $$ + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + SELECT count(*)::integer FROM regexp_matches($1, $2, 'pg'); +$$ +LANGUAGE 'sql' STRICT; + +-- REGEXP_COUNT( string text, pattern text, position int ) -> integer +CREATE OR REPLACE FUNCTION oracle.regexp_count(text, text, integer) +RETURNS integer +AS $$ +DECLARE + v_cnt integer; +BEGIN + -- Check numeric arguments + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + v_cnt := (SELECT count(*)::integer FROM regexp_matches(substr($1, $3), $2, 'pg')); + RETURN v_cnt; +END; +$$ +LANGUAGE plpgsql STRICT; + +-- REGEXP_COUNT( string text, pattern text, position int, flags text ) -> integer +CREATE OR REPLACE FUNCTION oracle.regexp_count(text, text, integer, text) +RETURNS integer +AS $$ +DECLARE + modifiers text; + v_cnt integer; +BEGIN + -- Only modifier can be NULL + IF $1 IS NULL OR $2 IS NULL OR $3 IS NULL THEN + RETURN NULL; + END IF; + -- Check numeric arguments + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + + modifiers := oracle.translate_oracle_modifiers($4, true); + v_cnt := (SELECT count(*)::integer FROM regexp_matches(substr($1, $3), $2, modifiers)); + RETURN v_cnt; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_INSTR( string text, pattern text ) -> integer +CREATE OR REPLACE FUNCTION oracle.regexp_instr(text, text) +RETURNS integer +AS $$ +DECLARE + v_pos integer; + v_pattern text; +BEGIN + -- Without subexpression specified, assume 0 which mean that the first + -- position for the substring matching the whole pattern is returned. + -- We need to enclose the pattern between parentheses. + v_pattern := '(' || $2 || ')'; + + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + v_pos := (SELECT position((SELECT (regexp_matches($1, v_pattern, 'pg'))[1] OFFSET 0 LIMIT 1) IN $1)); + + -- position() returns NULL when not found, we need to return 0 instead + IF v_pos IS NOT NULL THEN + RETURN v_pos; + END IF; + RETURN 0; +END; +$$ +LANGUAGE plpgsql STRICT; + +-- REGEXP_INSTR( string text, pattern text, position int ) -> integer +CREATE OR REPLACE FUNCTION oracle.regexp_instr(text, text, integer) +RETURNS integer +AS $$ +DECLARE + v_pos integer; + v_pattern text; +BEGIN + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + -- Without subexpression specified, assume 0 which mean that the first + -- position for the substring matching the whole pattern is returned. + -- We need to enclose the pattern between parentheses. + v_pattern := '(' || $2 || ')'; + + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + v_pos := (SELECT position((SELECT (regexp_matches(substr($1, $3), v_pattern, 'pg'))[1] OFFSET 0 LIMIT 1) IN $1)); + + -- position() returns NULL when not found, we need to return 0 instead + IF v_pos IS NOT NULL THEN + RETURN v_pos; + END IF; + RETURN 0; +END; +$$ +LANGUAGE plpgsql STRICT; + +-- REGEXP_INSTR( string text, pattern text, position int, occurence int ) -> integer +CREATE OR REPLACE FUNCTION oracle.regexp_instr(text, text, integer, integer) +RETURNS integer +AS $$ +DECLARE + v_pos integer; + v_pattern text; +BEGIN + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + IF $4 < 1 THEN + RAISE EXCEPTION 'argument ''occurence'' must be a number greater than 0'; + END IF; + + -- Without subexpression specified, assume 0 which mean that the first + -- position for the substring matching the whole pattern is returned. + -- We need to enclose the pattern between parentheses. + v_pattern := '(' || $2 || ')'; + + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + v_pos := (SELECT position((SELECT (regexp_matches(substr($1, $3), v_pattern, 'pg'))[1] OFFSET $4 - 1 LIMIT 1) IN $1)); + + -- position() returns NULL when not found, we need to return 0 instead + IF v_pos IS NOT NULL THEN + RETURN v_pos; + END IF; + RETURN 0; +END; +$$ +LANGUAGE plpgsql STRICT; + +-- REGEXP_INSTR( string text, pattern text, position int, occurence int, return_opt int ) -> integer +CREATE OR REPLACE FUNCTION oracle.regexp_instr(text, text, integer, integer, integer) +RETURNS integer +AS $$ +DECLARE + v_pos integer; + v_len integer; + v_pattern text; +BEGIN + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + IF $4 < 1 THEN + RAISE EXCEPTION 'argument ''occurence'' must be a number greater than 0'; + END IF; + IF $5 != 0 AND $5 != 1 THEN + RAISE EXCEPTION 'argument ''return_opt'' must be 0 or 1'; + END IF; + + -- Without subexpression specified, assume 0 which mean that the first + -- Without subexpression specified, assume 0 which mean that the first + -- position for the substring matching the whole pattern is returned. + -- We need to enclose the pattern between parentheses. + v_pattern := '(' || $2 || ')'; + + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + v_pos := (SELECT position((SELECT (regexp_matches(substr($1, $3), v_pattern, 'pg'))[1] OFFSET $4-1 LIMIT 1) IN $1)); + + -- position() returns NULL when not found, we need to return 0 instead + IF v_pos IS NOT NULL THEN + IF $5 = 1 THEN + v_len := (SELECT length((SELECT (regexp_matches(substr($1, $3), v_pattern, 'pg'))[1] OFFSET $4 - 1 LIMIT 1))); + v_pos := v_pos + v_len; + END IF; + RETURN v_pos; + END IF; + RETURN 0; +END; +$$ +LANGUAGE plpgsql STRICT; + +-- REGEXP_INSTR( string text, pattern text, position int, occurence int, return_opt int, flags text ) -> integer +CREATE OR REPLACE FUNCTION oracle.regexp_instr(text, text, integer, integer, integer, text) +RETURNS integer +AS $$ +DECLARE + v_pos integer; + v_len integer; + modifiers text; + v_pattern text; +BEGIN + -- Only modifier can be NULL + IF $1 IS NULL OR $2 IS NULL OR $3 IS NULL OR $4 IS NULL OR $5 IS NULL THEN + RETURN NULL; + END IF; + -- Check numeric arguments + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + IF $4 < 1 THEN + RAISE EXCEPTION 'argument ''occurence'' must be a number greater than 0'; + END IF; + IF $5 != 0 AND $5 != 1 THEN + RAISE EXCEPTION 'argument ''return_opt'' must be 0 or 1'; + END IF; + modifiers := oracle.translate_oracle_modifiers($6, true); + + -- Without subexpression specified, assume 0 which mean that the first + -- position for the substring matching the whole pattern is returned. + -- We need to enclose the pattern between parentheses. + v_pattern := '(' || $2 || ')'; + v_pos := (SELECT position((SELECT (regexp_matches(substr($1, $3), v_pattern, modifiers))[1] OFFSET $4 - 1 LIMIT 1) IN $1)); + + -- position() returns NULL when not found, we need to return 0 instead + IF v_pos IS NOT NULL THEN + IF $5 = 1 THEN + v_len := (SELECT length((SELECT (regexp_matches(substr($1, $3), v_pattern, modifiers))[1] OFFSET $4-1 LIMIT 1))); + v_pos := v_pos + v_len; + END IF; + RETURN v_pos; + END IF; + RETURN 0; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_INSTR( string text, pattern text, position int, occurence int, return_opt int, flags text, group int ) -> integer +CREATE OR REPLACE FUNCTION oracle.regexp_instr(text, text, integer, integer, integer, text, integer) +RETURNS integer +AS $$ +DECLARE + v_pos integer := 0; + v_pos_orig integer := $3; + v_len integer := 0; + modifiers text; + occurrence integer := $4; + idx integer := 1; + v_curr_pos integer := 0; + v_pattern text; + v_subexpr integer := $7; +BEGIN + -- Only modifier can be NULL + IF $1 IS NULL OR $2 IS NULL OR $3 IS NULL OR $4 IS NULL OR $5 IS NULL OR $7 IS NULL THEN + RETURN NULL; + END IF; + -- Check numeric arguments + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + IF $4 < 1 THEN + RAISE EXCEPTION 'argument ''occurence'' must be a number greater than 0'; + END IF; + IF $7 < 0 THEN + RAISE EXCEPTION 'argument ''group'' must be a positive number'; + END IF; + IF $5 != 0 AND $5 != 1 THEN + RAISE EXCEPTION 'argument ''return_opt'' must be 0 or 1'; + END IF; + + -- Translate Oracle regexp modifier into PostgreSQl ones + modifiers := oracle.translate_oracle_modifiers($6, true); + + -- If subexpression value is 0 we need to enclose the pattern between parentheses. + IF v_subexpr = 0 THEN + v_pattern := '(' || $2 || ')'; + v_subexpr := 1; + ELSE + v_pattern := $2; + END IF; + + -- To get position of occurrence > 1 we need a more complex code + LOOP + v_curr_pos := v_curr_pos + v_len; + v_pos := (SELECT position((SELECT (regexp_matches(substr($1, v_pos_orig), '('||$2||')', modifiers))[1] OFFSET 0 LIMIT 1) IN substr($1, v_pos_orig))); + v_len := (SELECT length((SELECT (regexp_matches(substr($1, v_pos_orig), '('||$2||')', modifiers))[1] OFFSET 0 LIMIT 1))); + + EXIT WHEN v_len IS NULL; + + v_pos_orig := v_pos_orig + v_pos + v_len; + v_curr_pos := v_curr_pos + v_pos; + idx := idx + 1; + + EXIT WHEN idx > occurrence; + END LOOP; + + v_pos := (SELECT position((SELECT (regexp_matches(substr($1, v_curr_pos), v_pattern, modifiers))[v_subexpr] OFFSET 0 LIMIT 1) IN substr($1, v_curr_pos))); + IF v_pos IS NOT NULL THEN + IF $5 = 1 THEN + v_len := (SELECT length((SELECT (regexp_matches(substr($1, v_curr_pos), v_pattern, modifiers))[v_subexpr] OFFSET 0 LIMIT 1))); + v_pos := v_pos + v_len; + END IF; + RETURN v_pos + v_curr_pos - 1; + END IF; + RETURN 0; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_SUBSTR( string text, pattern text ) -> text +CREATE OR REPLACE FUNCTION oracle.regexp_substr(text, text) +RETURNS text +AS $$ +DECLARE + v_substr text; + v_pattern text; +BEGIN + -- Without subexpression specified, assume 0 which mean that the first + -- position for the substring matching the whole pattern is returned. + -- We need to enclose the pattern between parentheses. + v_pattern := '(' || $2 || ')'; + + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + v_substr := (SELECT (regexp_matches($1, v_pattern, 'pg'))[1] OFFSET 0 LIMIT 1); + RETURN v_substr; +END; +$$ +LANGUAGE plpgsql STRICT; + +-- REGEXP_SUBSTR( string text, pattern text, position int ) -> text +CREATE OR REPLACE FUNCTION oracle.regexp_substr(text, text, int) +RETURNS text +AS $$ +DECLARE + v_substr text; + v_pattern text; +BEGIN + -- Check numeric arguments + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + + -- Without subexpression specified, assume 0 which mean that the first + -- position for the substring matching the whole pattern is returned. + -- We need to enclose the pattern between parentheses. + v_pattern := '(' || $2 || ')'; + + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + v_substr := (SELECT (regexp_matches(substr($1, $3), v_pattern, 'pg'))[1] OFFSET 0 LIMIT 1); + RETURN v_substr; +END; +$$ +LANGUAGE plpgsql STRICT; + +-- REGEXP_SUBSTR( string text, pattern text, position int, occurence int ) -> text +CREATE OR REPLACE FUNCTION oracle.regexp_substr(text, text, integer, integer) +RETURNS text +AS $$ +DECLARE + v_substr text; + v_pattern text; +BEGIN + -- Check numeric arguments + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + IF $4 < 1 THEN + RAISE EXCEPTION 'argument ''occurence'' must be a number greater than 0'; + END IF; + + -- Without subexpression specified, assume 0 which mean that the first + -- position for the substring matching the whole pattern is returned. + -- We need to enclose the pattern between parentheses. + v_pattern := '(' || $2 || ')'; + + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + v_substr := (SELECT (regexp_matches(substr($1, $3), v_pattern, 'pg'))[1] OFFSET $4 - 1 LIMIT 1); + RETURN v_substr; +END; +$$ +LANGUAGE plpgsql STRICT; + +-- REGEXP_SUBSTR( string text, pattern text, position int, occurence int, flags text ) -> text +CREATE OR REPLACE FUNCTION oracle.regexp_substr(text, text, integer, integer, text) +RETURNS text +AS $$ +DECLARE + v_substr text; + v_pattern text; + modifiers text; +BEGIN + -- Only modifier can be NULL + IF $1 IS NULL OR $2 IS NULL OR $3 IS NULL OR $4 IS NULL THEN + RETURN NULL; + END IF; + -- Check numeric arguments + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + IF $4 < 1 THEN + RAISE EXCEPTION 'argument ''occurence'' must be a number greater than 0'; + END IF; + + modifiers := oracle.translate_oracle_modifiers($5, true); + + -- Without subexpression specified, assume 0 which mean that the first + -- position for the substring matching the whole pattern is returned. + -- We need to enclose the pattern between parentheses. + v_pattern := '(' || $2 || ')'; + + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + v_substr := (SELECT (regexp_matches(substr($1, $3), v_pattern, modifiers))[1] OFFSET $4 - 1 LIMIT 1); + RETURN v_substr; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_SUBSTR( string text, pattern text, position int, occurence int, flags text, group int ) -> text +CREATE OR REPLACE FUNCTION oracle.regexp_substr(text, text, integer, integer, text, int) +RETURNS text +AS $$ +DECLARE + v_substr text; + v_pattern text; + modifiers text; + v_subexpr integer := $6; + has_group integer; +BEGIN + -- Only modifier can be NULL + IF $1 IS NULL OR $2 IS NULL OR $3 IS NULL OR $4 IS NULL OR $6 IS NULL THEN + RETURN NULL; + END IF; + -- Check numeric arguments + IF $3 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + IF $4 < 1 THEN + RAISE EXCEPTION 'argument ''occurence'' must be a number greater than 0'; + END IF; + IF v_subexpr < 0 THEN + RAISE EXCEPTION 'argument ''group'' must be a positive number'; + END IF; + + -- Check that with v_subexpr = 1 we have a capture group otherwise return NULL + has_group := (SELECT count(*) FROM regexp_matches($2, '(?:[^\\]|^)\(', 'g')); + IF $6 = 1 AND has_group = 0 THEN + RETURN NULL; + END IF; + + modifiers := oracle.translate_oracle_modifiers($5, true); + + -- If subexpression value is 0 we need to enclose the pattern between parentheses. + IF v_subexpr = 0 THEN + v_pattern := '(' || $2 || ')'; + v_subexpr := 1; + ELSE + v_pattern := $2; + END IF; + + -- Oracle default behavior is newline-sensitive, + -- PostgreSQL not, so force 'p' modifier to affect + -- newline-sensitivity but not ^ and $ search. + v_substr := (SELECT (regexp_matches(substr($1, $3), v_pattern, modifiers))[v_subexpr] OFFSET $4 - 1 LIMIT 1); + RETURN v_substr; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_REPLACE( string text, pattern text, replace_string text ) -> text +CREATE OR REPLACE FUNCTION oracle.regexp_replace(text, text, text) +RETURNS text +AS $$ +DECLARE + str text; +BEGIN + IF $2 IS NULL AND $1 IS NOT NULL THEN + RETURN $1; + END IF; + -- Oracle default behavior is to replace all occurence + -- whereas PostgreSQL only replace the first occurrence + -- so we need to add 'g' modifier. + SELECT pg_catalog.regexp_replace($1, $2, $3, 'g') INTO str; + RETURN str; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_REPLACE( string text, pattern text, replace_string text, position int ) -> text +CREATE OR REPLACE FUNCTION oracle.regexp_replace(text, text, text, integer) +RETURNS text +AS $$ +DECLARE + v_replaced_str text; + v_before text; +BEGIN + IF $1 IS NULL OR $3 IS NULL OR $4 IS NULL THEN + RETURN NULL; + END IF; + IF $2 IS NULL THEN + RETURN $1; + END IF; + -- Check numeric arguments + IF $4 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + + v_before = substr($1, 1, $4 - 1); + + -- Oracle default behavior is to replace all occurence + -- whereas PostgreSQL only replace the first occurrence + -- so we need to add 'g' modifier. + v_replaced_str := v_before || pg_catalog.regexp_replace(substr($1, $4), $2, $3, 'g'); + RETURN v_replaced_str; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_REPLACE( string text, pattern text, replace_string text, position int, occurence int ) -> text +CREATE OR REPLACE FUNCTION oracle.regexp_replace(text, text, text, integer, integer) +RETURNS text +AS $$ +DECLARE + v_replaced_str text; + v_pos integer := $4; + v_before text := ''; + v_nummatch integer; +BEGIN + IF $1 IS NULL OR $3 IS NULL OR $4 IS NULL OR $5 IS NULL THEN + RETURN NULL; + END IF; + IF $2 IS NULL THEN + RETURN $1; + END IF; + -- Check numeric arguments + IF $4 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + IF $5 < 0 THEN + RAISE EXCEPTION 'argument ''occurrence'' must be a positive number'; + END IF; + -- Check if the occurrence queried exceeds the number of occurrences + IF $5 > 1 THEN + v_nummatch := (SELECT count(*) FROM regexp_matches(substr($1, $4), $2, 'g')); + IF $5 > v_nummatch THEN + RETURN $1; + END IF; + -- Get the position of the occurrence we are looking for + v_pos := oracle.regexp_instr($1, $2, $4, $5, 0, '', 1); + IF v_pos = 0 THEN + RETURN $1; + END IF; + END IF; + -- Get the substring before this position we will need to restore it + v_before := substr($1, 1, v_pos - 1); + + -- Replace all occurrences + IF $5 = 0 THEN + v_replaced_str := v_before || pg_catalog.regexp_replace(substr($1, v_pos), $2, $3, 'g'); + ELSE + -- Replace the first occurrence + v_replaced_str := v_before || pg_catalog.regexp_replace(substr($1, v_pos), $2, $3); + END IF; + + RETURN v_replaced_str; +END; +$$ +LANGUAGE plpgsql; + +-- REGEXP_REPLACE( string text, pattern text, replace_string text, position int, occurence int, flags text ) -> text +CREATE OR REPLACE FUNCTION oracle.regexp_replace(text, text, text, integer, integer, text) +RETURNS text +AS $$ +DECLARE + v_replaced_str text; + v_pos integer := $4; + v_nummatch integer; + v_before text := ''; + modifiers text := ''; +BEGIN + IF $1 IS NULL OR $3 IS NULL OR $4 IS NULL OR $5 IS NULL THEN + RETURN NULL; + END IF; + IF $2 IS NULL THEN + RETURN $1; + END IF; + -- Check numeric arguments + IF $4 < 1 THEN + RAISE EXCEPTION 'argument ''position'' must be a number greater than 0'; + END IF; + IF $5 < 0 THEN + RAISE EXCEPTION 'argument ''occurrence'' must be a positive number'; + END IF; + -- Set the modifiers + IF $5 = 0 THEN + modifiers := oracle.translate_oracle_modifiers($6, true); + ELSE + modifiers := oracle.translate_oracle_modifiers($6, false); + END IF; + -- Check if the occurrence queried exceeds the number of occurrences + IF $5 > 1 THEN + v_nummatch := (SELECT count(*) FROM regexp_matches(substr($1, $4), $2, $6||'g')); + IF $5 > v_nummatch THEN + RETURN $1; + END IF; + -- Get the position of the occurrence we are looking for + v_pos := oracle.regexp_instr($1, $2, $4, $5, 0, $6, 1); + IF v_pos = 0 THEN + RETURN $1; + END IF; + END IF; + -- Get the substring before this position we will need to restore it + v_before := substr($1, 1, v_pos - 1); + -- Replace occurrence(s) + v_replaced_str := v_before || pg_catalog.regexp_replace(substr($1, v_pos), $2, $3, modifiers); + RETURN v_replaced_str; +END; +$$ +LANGUAGE plpgsql; diff --git a/contrib/orafce/orafce--3.2--3.3.sql b/contrib/orafce/orafce--3.2--3.3.sql new file mode 100644 index 000000000..b3a732fb7 --- /dev/null +++ b/contrib/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/contrib/orafce/orafce--3.3--3.4.sql b/contrib/orafce/orafce--3.3--3.4.sql new file mode 100644 index 000000000..948c2ec5e --- /dev/null +++ b/contrib/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/contrib/orafce/orafce--3.4--3.5.sql b/contrib/orafce/orafce--3.4--3.5.sql new file mode 100644 index 000000000..e035a69a3 --- /dev/null +++ b/contrib/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/contrib/orafce/orafce--3.5--3.6.sql b/contrib/orafce/orafce--3.5--3.6.sql new file mode 100644 index 000000000..50ea3a9d0 --- /dev/null +++ b/contrib/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/contrib/orafce/orafce--3.6--3.7.sql b/contrib/orafce/orafce--3.6--3.7.sql new file mode 100644 index 000000000..e0339fae5 --- /dev/null +++ b/contrib/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/contrib/orafce/orafce--3.7--3.8.sql b/contrib/orafce/orafce--3.7--3.8.sql new file mode 100644 index 000000000..11716b9e5 --- /dev/null +++ b/contrib/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/contrib/orafce/orafce--3.8--3.9.sql b/contrib/orafce/orafce--3.8--3.9.sql new file mode 100644 index 000000000..9ba0b71b6 --- /dev/null +++ b/contrib/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/contrib/orafce/orafce--3.9--3.10.sql b/contrib/orafce/orafce--3.9--3.10.sql new file mode 100644 index 000000000..3ade6de4f --- /dev/null +++ b/contrib/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/contrib/orafce/orafce.c b/contrib/orafce/orafce.c new file mode 100644 index 000000000..20006cea9 --- /dev/null +++ b/contrib/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/contrib/orafce/orafce.control b/contrib/orafce/orafce.control new file mode 100644 index 000000000..f81859d83 --- /dev/null +++ b/contrib/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.17' +module_pathname = '$libdir/orafce' +relocatable = false diff --git a/contrib/orafce/orafce.h b/contrib/orafce/orafce.h new file mode 100644 index 000000000..0ebf6842e --- /dev/null +++ b/contrib/orafce/orafce.h @@ -0,0 +1,65 @@ +#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 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/contrib/orafce/others.c b/contrib/orafce/others.c new file mode 100644 index 000000000..e68abefeb --- /dev/null +++ b/contrib/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/contrib/orafce/parallel_schedule b/contrib/orafce/parallel_schedule new file mode 100644 index 000000000..164f7361c --- /dev/null +++ b/contrib/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/contrib/orafce/parse_keyword.c b/contrib/orafce/parse_keyword.c new file mode 100644 index 000000000..146c27e25 --- /dev/null +++ b/contrib/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/contrib/orafce/parse_keyword.h b/contrib/orafce/parse_keyword.h new file mode 100644 index 000000000..d39fd42aa --- /dev/null +++ b/contrib/orafce/parse_keyword.h @@ -0,0 +1,2 @@ + +extern const char *orafce_scan_keyword(const char *text, int *keycode); diff --git a/contrib/orafce/pipe.c b/contrib/orafce/pipe.c new file mode 100644 index 000000000..f2dd39e09 --- /dev/null +++ b/contrib/orafce/pipe.c @@ -0,0 +1,1365 @@ +#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; +} orafce_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 + + orafce_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; + +orafce_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(orafce_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 orafce_pipe* +find_pipe(text* pipe_name, bool* created, bool only_check) +{ + int i; + orafce_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(orafce_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(orafce_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); + + if (p->creator) + { + ora_sfree(p->creator); + p->creator = NULL; + } + + p->is_valid = false; + } + + } + + return ptr; +} + + +/* copy message to local memory, if exists */ + +static message_buffer* +get_from_pipe(text *pipe_name, bool *found) +{ + orafce_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) +{ + orafce_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) +{ + orafce_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; + + if (p->creator) + { + ora_sfree(p->creator); + p->creator = NULL; + } + + } + } +} + + +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) +{ +#if PG_VERSION_NUM >= 120000 + + 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; + +#else + + info->arg[0] = arg0; + info->arg[1] = arg1; + info->arg[2] = arg2; + info->argnull[0] = false; + info->argnull[1] = false; + info->argnull[2] = false; + +#endif +} + + +/* + * 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; + +#if PG_VERSION_NUM >= 120000 + + LOCAL_FCINFO(info, 3); + +#else + + FunctionCallInfoData info_data; + FunctionCallInfo info = &info_data; + +#endif + + + 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: + { +#if PG_VERSION_NUM >= 120000 + + LOCAL_FCINFO(info, 3); + +#else + + FunctionCallInfoData info_data; + FunctionCallInfo info = &info_data; + +#endif + + 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)) + { + orafce_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/contrib/orafce/pipe.h b/contrib/orafce/pipe.h new file mode 100644 index 000000000..17eb5ba63 --- /dev/null +++ b/contrib/orafce/pipe.h @@ -0,0 +1,50 @@ +#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; + int pid; + 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/contrib/orafce/plunit.c b/contrib/orafce/plunit.c new file mode 100644 index 000000000..e03771fb1 --- /dev/null +++ b/contrib/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/contrib/orafce/plvdate.c b/contrib/orafce/plvdate.c new file mode 100644 index 000000000..c8b6c0e74 --- /dev/null +++ b/contrib/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/contrib/orafce/plvlex.c b/contrib/orafce/plvlex.c new file mode 100644 index 000000000..c55bfaf30 --- /dev/null +++ b/contrib/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/contrib/orafce/plvlex.h b/contrib/orafce/plvlex.h new file mode 100644 index 000000000..f56402355 --- /dev/null +++ b/contrib/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/contrib/orafce/plvstr.c b/contrib/orafce/plvstr.c new file mode 100644 index 000000000..74ded5d5a --- /dev/null +++ b/contrib/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/contrib/orafce/plvsubst.c b/contrib/orafce/plvsubst.c new file mode 100644 index 000000000..8a181bf68 --- /dev/null +++ b/contrib/orafce/plvsubst.c @@ -0,0 +1,277 @@ +/* + 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; + +#if PG_VERSION_NUM >= 120000 + + LOCAL_FCINFO(locfcinfo, 2); + +#else + + FunctionCallInfoData locfcinfo_data; + FunctionCallInfo locfcinfo = &locfcinfo_data; + +#endif + + 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); + +#if PG_VERSION_NUM >= 120000 + + 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; + +#else + + locfcinfo->arg[0] = PG_GETARG_DATUM(1); + locfcinfo->arg[1] = PG_GETARG_IF_EXISTS(2, DATUM, CStringGetTextDatum(",")); + locfcinfo->argnull[0] = false; + locfcinfo->argnull[1] = false; + +#endif + + 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/contrib/orafce/putline.c b/contrib/orafce/putline.c new file mode 100644 index 000000000..59b64469c --- /dev/null +++ b/contrib/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/contrib/orafce/random.c b/contrib/orafce/random.c new file mode 100644 index 000000000..858db955d --- /dev/null +++ b/contrib/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/contrib/orafce/replace_empty_string.c b/contrib/orafce/replace_empty_string.c new file mode 100644 index 000000000..365641d58 --- /dev/null +++ b/contrib/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/contrib/orafce/shmmc.c b/contrib/orafce/shmmc.c new file mode 100644 index 000000000..a51f5e508 --- /dev/null +++ b/contrib/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/contrib/orafce/shmmc.h b/contrib/orafce/shmmc.h new file mode 100644 index 000000000..2bf26b96a --- /dev/null +++ b/contrib/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/contrib/orafce/sql/aggregates.sql b/contrib/orafce/sql/aggregates.sql new file mode 100644 index 000000000..3b8483876 --- /dev/null +++ b/contrib/orafce/sql/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/contrib/orafce/sql/dbms_alert_session_A.sql b/contrib/orafce/sql/dbms_alert_session_A.sql new file mode 100644 index 000000000..18cbb6b60 --- /dev/null +++ b/contrib/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/contrib/orafce/sql/dbms_alert_session_B.sql b/contrib/orafce/sql/dbms_alert_session_B.sql new file mode 100644 index 000000000..f7f300830 --- /dev/null +++ b/contrib/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/contrib/orafce/sql/dbms_alert_session_C.sql b/contrib/orafce/sql/dbms_alert_session_C.sql new file mode 100644 index 000000000..17b327b6b --- /dev/null +++ b/contrib/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/contrib/orafce/sql/dbms_output.sql b/contrib/orafce/sql/dbms_output.sql new file mode 100644 index 000000000..c46088ce3 --- /dev/null +++ b/contrib/orafce/sql/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; +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; +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; +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; +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; +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; +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; +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; +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; +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; +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; +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; +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; +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; +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; +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; +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; +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; +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; +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; +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/contrib/orafce/sql/dbms_pipe.sql b/contrib/orafce/sql/dbms_pipe.sql new file mode 100644 index 000000000..853b9d169 --- /dev/null +++ b/contrib/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/contrib/orafce/sql/dbms_pipe_session_A.sql b/contrib/orafce/sql/dbms_pipe_session_A.sql new file mode 100644 index 000000000..8ecf41a67 --- /dev/null +++ b/contrib/orafce/sql/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/contrib/orafce/sql/dbms_pipe_session_B.sql b/contrib/orafce/sql/dbms_pipe_session_B.sql new file mode 100644 index 000000000..0d3cad732 --- /dev/null +++ b/contrib/orafce/sql/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/contrib/orafce/sql/dbms_random.sql b/contrib/orafce/sql/dbms_random.sql new file mode 100644 index 000000000..94bcf3f92 --- /dev/null +++ b/contrib/orafce/sql/dbms_random.sql @@ -0,0 +1,16 @@ +-- Tests for package DBMS_RANDOM +SELECT dbms_random.initialize(8); +SELECT dbms_random.normal()::numeric(10, 8); +SELECT dbms_random.normal()::numeric(10, 8); +SELECT dbms_random.seed(8); +SELECT dbms_random.random(); +SELECT dbms_random.seed('test'); +SELECT dbms_random.string('U',5); +SELECT dbms_random.string('P',2); +SELECT dbms_random.string('x',4); +SELECT dbms_random.string('a',2); +SELECT dbms_random.string('l',3); +SELECT dbms_random.seed(5); +SELECT dbms_random.value()::numeric(10, 8); +SELECT dbms_random.value(10,15)::numeric(10, 8); +SELECT dbms_random.terminate(); diff --git a/contrib/orafce/sql/dbms_utility.sql b/contrib/orafce/sql/dbms_utility.sql new file mode 100644 index 000000000..b1c76ee3b --- /dev/null +++ b/contrib/orafce/sql/dbms_utility.sql @@ -0,0 +1,82 @@ +\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(); + +/* + * Test for dbms_utility.get_time(), the result is rounded + * to have constant result in the regression test. + */ +DO $$ +DECLARE + start_time integer; + end_time integer; +BEGIN + start_time := DBMS_UTILITY.GET_TIME(); + PERFORM pg_sleep(2); + end_time := DBMS_UTILITY.GET_TIME(); + -- clamp long runtime on slow build machines to the 2s the testsuite is expecting + IF end_time BETWEEN start_time + 300 AND start_time + 1000 THEN end_time := start_time + 250; END IF; + RAISE NOTICE 'Execution time: % seconds', trunc((end_time - start_time)::numeric/100); +END +$$; + + diff --git a/contrib/orafce/sql/files.sql b/contrib/orafce/sql/files.sql new file mode 100644 index 000000000..1082b4fe6 --- /dev/null +++ b/contrib/orafce/sql/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/contrib/orafce/sql/init.sql b/contrib/orafce/sql/init.sql new file mode 100644 index 000000000..d203168f0 --- /dev/null +++ b/contrib/orafce/sql/init.sql @@ -0,0 +1,5 @@ +\set ECHO none +set client_min_messages TO error; +CREATE EXTENSION IF NOT EXISTS orafce; +GRANT ALL ON SCHEMA public TO public; +set client_min_messages TO default; \ No newline at end of file diff --git a/contrib/orafce/sql/nlssort.sql b/contrib/orafce/sql/nlssort.sql new file mode 100644 index 000000000..9cfb2f1f2 --- /dev/null +++ b/contrib/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/contrib/orafce/sql/nvarchar2.sql b/contrib/orafce/sql/nvarchar2.sql new file mode 100644 index 000000000..19095edd8 --- /dev/null +++ b/contrib/orafce/sql/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/contrib/orafce/sql/orafce.sql b/contrib/orafce/sql/orafce.sql new file mode 100644 index 000000000..89db62693 --- /dev/null +++ b/contrib/orafce/sql/orafce.sql @@ -0,0 +1,1033 @@ +\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; + +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; + +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/contrib/orafce/sql/orafce2.sql b/contrib/orafce/sql/orafce2.sql new file mode 100644 index 000000000..5f5bde943 --- /dev/null +++ b/contrib/orafce/sql/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/contrib/orafce/sql/regexp_func.sql b/contrib/orafce/sql/regexp_func.sql new file mode 100644 index 000000000..ff5541454 --- /dev/null +++ b/contrib/orafce/sql/regexp_func.sql @@ -0,0 +1,297 @@ +-- Test for the regexp_*() function +\set ECHO none +SET client_min_messages = warning; +SET client_encoding = utf8; +\set VERBOSITY terse +\set ECHO all + +SET search_path TO oracle,"$user", public, pg_catalog; + +---- +-- Tests for REGEXP_LIKE() +---- + +-- ORACLE> SELECT 1 FROM DUAL WHERE REGEXP_LIKE('a'||CHR(10)||'d', 'a.d'); -> NULL +SELECT REGEXP_LIKE('a'||CHR(10)||'d', 'a.d'); +-- ORACLE> SELECT 1 FROM DUAL WHERE REGEXP_LIKE('a'||CHR(10)||'d', 'a.d', 'm'); -> NULL +SELECT REGEXP_LIKE('a'||CHR(10)||'d', 'a.d', 'm'); +-- ORACLE> SELECT 1 FROM DUAL WHERE REGEXP_LIKE('a'||CHR(10)||'d', 'a.d', 'n'); -> 1 +SELECT REGEXP_LIKE('a'||CHR(10)||'d', 'a.d', 'n'); +-- ORACLE> SELECT 1 FROM DUAL WHERE REGEXP_LIKE('Steven', '^Ste(v|ph)en$'); -> 1 +SELECT REGEXP_LIKE('Steven', '^Ste(v|ph)en$'); +-- ORACLE> SELECT 1 FROM DUAL WHERE regexp_like('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^bar'); -> NULL +SELECT REGEXP_LIKE('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^bar'); +-- ORACLE> SELECT 1 FROM DUAL WHERE regexp_like('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', 'bar'); -> 1 +SELECT REGEXP_LIKE('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', 'bar'); +-- ORACLE> SELECT 1 FROM DUAL WHERE regexp_like('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^bar', 'm'); -> 1 +SELECT REGEXP_LIKE('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^bar', 'm'); +-- ORACLE> SELECT 1 FROM DUAL WHERE regexp_like('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^bar', 'n'); -> NULL +SELECT REGEXP_LIKE('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^bar', 'n'); +-- ORACLE> SELECT 1 FROM DUAL WHERE REGEXP_LIKE('GREEN', '([aeiou])\1'); -> NULL +SELECT REGEXP_LIKE('GREEN', '([aeiou])\1'); +-- ORACLE> SELECT 1 FROM DUAL WHERE REGEXP_LIKE('GREEN', '([aeiou])\1', 'i'); -> 1 +SELECT REGEXP_LIKE('GREEN', '([aeiou])\1', 'i'); +-- ORACLE> SELECT 1 FROM DUAL WHERE REGEXP_LIKE('ORANGE' || chr(10) || 'GREEN', '([aeiou])\1', 'i'); -> 1 +SELECT REGEXP_LIKE('ORANGE' || chr(10) || 'GREEN', '([aeiou])\1', 'i'); +-- ORACLE> SELECT 1 FROM DUAL WHERE REGEXP_LIKE('ORANGE' || chr(10) || 'GREEN', '^..([aeiou])\1', 'i'); -> NULL +SELECT REGEXP_LIKE('ORANGE' || chr(10) || 'GREEN', '^..([aeiou])\1', 'i'); +-- ORACLE> SELECT 1 FROM DUAL WHERE REGEXP_LIKE('ORANGE' || chr(10) || 'GREEN', '([aeiou])\1', 'in'); -> 1 +SELECT REGEXP_LIKE('ORANGE' || chr(10) || 'GREEN', '([aeiou])\1', 'in'); +-- ORACLE> SELECT 1 FROM DUAL WHERE REGEXP_LIKE('ORANGE' || chr(10) || 'GREEN', '^..([aeiou])\1', 'in'); -> NULL +SELECT REGEXP_LIKE('ORANGE' || chr(10) || 'GREEN', '^..([aeiou])\1', 'in'); +-- ORACLE> SELECT 1 FROM DUAL WHERE REGEXP_LIKE('ORANGE' || chr(10) || 'GREEN', '^..([aeiou])\1', 'im'); -> 1 +SELECT REGEXP_LIKE('ORANGE' || chr(10) || 'GREEN', '^..([aeiou])\1', 'im'); + +---- +-- Tests for REGEXP_COUNT() +---- + +-- ORACLE> SELECT REGEXP_COUNT('a'||CHR(10)||'d', 'a.d') FROM DUAL; -> 0 +SELECT REGEXP_COUNT('a'||CHR(10)||'d', 'a.d'); +-- ORACLE> SELECT REGEXP_COUNT('a'||CHR(10)||'d', 'a.d', 1, 'm') FROM DUAL; -> 0 +SELECT REGEXP_COUNT('a'||CHR(10)||'d', 'a.d', 1, 'm'); +-- ORACLE> SELECT REGEXP_COUNT('a'||CHR(10)||'d', 'a.d', 1, 'n') FROM DUAL; -> 1 +SELECT REGEXP_COUNT('a'||CHR(10)||'d', 'a.d', 1, 'n'); +-- ORACLE> SELECT REGEXP_COUNT('Steven', '^Ste(v|ph)en$') FROM DUAL; -> 1 +SELECT REGEXP_COUNT('Steven', '^Ste(v|ph)en$'); +-- ORACLE> SELECT REGEXP_COUNT('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^bar') FROM DUAL; -> 0 +SELECT REGEXP_COUNT('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^bar'); +-- ORACLE> SELECT REGEXP_COUNT('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', 'bar') FROM DUAL; -> 1 +SELECT REGEXP_COUNT('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', 'bar'); +-- ORACLE> SELECT REGEXP_COUNT('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^bar', 0, 'm') FROM DUAL; -> ORA-01428: argument '0' is out of range +SELECT REGEXP_COUNT('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^bar', 0, 'm'); +-- ORACLE> SELECT REGEXP_COUNT('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^bar', 1, 'm') FROM DUAL; -> 1 +SELECT REGEXP_COUNT('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^bar', 1, 'm'); +-- ORACLE> SELECT REGEXP_COUNT('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^bar', 1, 'n') FROM DUAL; -> 0 +SELECT REGEXP_COUNT('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^bar', 1, 'n'); +-- ORACLE> SELECT REGEXP_COUNT('GREEN', '([aeiou])\1') FROM DUAL; -> 0 +SELECT REGEXP_COUNT('GREEN', '([aeiou])\1'); +-- ORACLE> SELECT REGEXP_COUNT('GREEN', '([aeiou])\1', 1, 'i') FROM DUAL; -> 1 +SELECT REGEXP_COUNT('GREEN', '([aeiou])\1', 1, 'i'); +-- ORACLE> SELECT REGEXP_COUNT('ORANGE' || chr(10) || 'GREEN', '([aeiou])\1', 1, 'i') FROM DUAL; -> 1 +SELECT REGEXP_COUNT('ORANGE' || chr(10) || 'GREEN', '([aeiou])\1', 1, 'i'); +-- ORACLE> SELECT REGEXP_COUNT('ORANGE' || chr(10) || 'GREEN', '^..([aeiou])\1', 1, 'i') FROM DUAL; -> 0 +SELECT REGEXP_COUNT('ORANGE' || chr(10) || 'GREEN', '^..([aeiou])\1', 1, 'i'); +-- ORACLE> SELECT REGEXP_COUNT('ORANGE' || chr(10) || 'GREEN', '([aeiou])\1', 1, 'in') FROM DUAL; -> 1 +SELECT REGEXP_COUNT('ORANGE' || chr(10) || 'GREEN', '([aeiou])\1', 1, 'in'); +-- ORACLE> SELECT REGEXP_COUNT('ORANGE' || chr(10) || 'GREEN', '^..([aeiou])\1', 1, 'in') FROM DUAL; -> 0 +SELECT REGEXP_COUNT('ORANGE' || chr(10) || 'GREEN', '^..([aeiou])\1', 1, 'in'); +-- ORACLE> SELECT REGEXP_COUNT('ORANGE' || chr(10) || 'GREEN', '^..([aeiou])\1', 1, 'im') FROM DUAL; -> 1 +SELECT REGEXP_COUNT('ORANGE' || chr(10) || 'GREEN', '^..([aeiou])\1', 1, 'im'); +-- ORACLE> SELECT REGEXP_COUNT('123123123123123', '(12)3', 1, 'i') REGEXP_COUNT FROM DUAL; -> 5 +SELECT REGEXP_COUNT('123123123123123', '(12)3', 1, 'i'); +-- ORACLE> SELECT REGEXP_COUNT('123123123123', '123', 3, 'i') COUNT FROM DUAL; -> 3 +SELECT REGEXP_COUNT('123123123123', '123', 3, 'i'); +-- ORACLE> SELECT REGEXP_COUNT('ABC123', '[A-Z]'), REGEXP_COUNT('A1B2C3', '[A-Z]') FROM DUAL; -> 3 | 3 +SELECT REGEXP_COUNT('ABC123', '[A-Z]'), oracle.REGEXP_COUNT('A1B2C3', '[A-Z]'); +-- ORACLE> SELECT REGEXP_COUNT('ABC123', '[A-Z][0-9]'), REGEXP_COUNT('A1B2C3', '[A-Z][0-9]') FROM DUAL; -> 1 | 3 +SELECT REGEXP_COUNT('ABC123', '[A-Z][0-9]'), oracle.REGEXP_COUNT('A1B2C3', '[A-Z][0-9]'); +-- ORACLE> SELECT REGEXP_COUNT('ABC123', '^[A-Z][0-9]'), REGEXP_COUNT('A1B2C3', '^[A-Z][0-9]') FROM DUAL; -> 0 | 1 +SELECT REGEXP_COUNT('ABC123', '^[A-Z][0-9]'), oracle.REGEXP_COUNT('A1B2C3', '^[A-Z][0-9]'); +-- ORACLE> SELECT REGEXP_COUNT('ABC123', '([A-Z][0-9]){2}'), REGEXP_COUNT('A1B2C3', '([A-Z][0-9]){2}') FROM DUAL; -> 0 | 1 +SELECT REGEXP_COUNT('ABC123', '([A-Z][0-9]){2}'), oracle.REGEXP_COUNT('A1B2C3', '([A-Z][0-9]){2}'); +-- Check negatives values that must throw an error +SELECT REGEXP_COUNT('ORANGE' || chr(10) || 'GREEN', '^..([aeiou])\1', -1, 'i'); + +---- +-- Tests for REGEXP_INSTR() +---- + +-- ORACLE> SELECT REGEXP_INSTR('1234567890', '(123)(4(56)(78))') FROM DUAL; -> 1 +SELECT REGEXP_INSTR('1234567890', '(123)(4(56)(78))'); +-- ORACLE> SELECT REGEXP_INSTR('1234567890', '(4(56)(78))') FROM DUAL; -> 4 +SELECT REGEXP_INSTR('1234567890', '(4(56)(78))'); +-- ORACLE> SELECT regexp_instr('1234567890', '123(4(56)(78))', 3) FROM DUAL; -> 0 +SELECT REGEXP_INSTR('1234567890', '(123)(4(56)(78))', 3); +-- ORACLE> SELECT REGEXP_INSTR('1234567890', '(4(56)(78))', 3) FROM DUAL; -> 4 +SELECT REGEXP_INSTR('1234567890', '(4(56)(78))', 3); +-- ORACLE> SELECT REGEXP_INSTR('500 Oracle Parkway, Redwood Shores, CA', '[^ ]+', 1, 6) FROM DUAL; -> 37 +SELECT REGEXP_INSTR('500 Oracle Parkway, Redwood Shores, CA', '[^ ]+', 1, 6); +-- ORACLE> SELECT REGEXP_INSTR('500 Oracle Parkway, Redwood Shores, CA', '[S|R|P][[:alpha:]]{6}', 3, 2, 0) FROM DUAL; -> 21 +SELECT REGEXP_INSTR('500 Oracle Parkway, Redwood Shores, CA', '[S|R|P][[:alpha:]]{6}', 3, 2, 0); +-- ORACLE> SELECT REGEXP_INSTR('500 Oracle Parkway, Redwood Shores, CA', '[S|R|P][[:alpha:]]{6}', 3, 2, 1) FROM DUAL; -> 28 +SELECT REGEXP_INSTR('500 Oracle Parkway, Redwood Shores, CA', '[S|R|P][[:alpha:]]{6}', 3, 2, 1); +-- ORACLE> SELECT REGEXP_INSTR('500 Oracle Parkway, Redwood Shores, CA', '[s|r|p][[:alpha:]]{6}', 3, 2, 1, 'i') FROM DUAL; -> 28 +SELECT REGEXP_INSTR('500 Oracle Parkway, Redwood Shores, CA', '[q|r|p][[:alpha:]]{6}', 3, 2, 1, 'i'); +-- ORACLE> SELECT REGEXP_INSTR('1234567890', '(123)(4(56)(78))', 1, 1, 0, 'i', 0) FROM DUAL; -> 1 +SELECT REGEXP_INSTR('1234567890', '(123)(4(56)(78))', 1, 1, 0, 'i', 0); +-- ORACLE> SELECT REGEXP_INSTR('1234567890', '(123)(4(56)(78))', 1, 1, 0, 'i', 1) FROM DUAL; -> 1 +SELECT REGEXP_INSTR('1234567890', '(123)(4(56)(78))', 1, 1, 0, 'i', 1); +-- ORACLE> SELECT REGEXP_INSTR('1234567890', '(123)(4(56)(78))', 1, 1, 0, 'i', 2) FROM DUAL; -> 4 +SELECT REGEXP_INSTR('1234567890', '(123)(4(56)(78))', 1, 1, 0, 'i', 2); +-- ORACLE> SELECT REGEXP_INSTR('1234567890', '(123)(4(56)(78))', 1, 1, 0, 'i', 4) FROM DUAL; -> 7 +SELECT REGEXP_INSTR('1234567890', '(123)(4(56)(78))', 1, 1, 0, 'i', 4); +-- ORACLE> SELECT REGEXP_INSTR('1234567890 1234567890', '(123)(4(56)(78))', 1, 1, 0, 'i', 4) FROM DUAL; -> 7 +SELECT REGEXP_INSTR('1234567890 1234567890', '(123)(4(56)(78))', 1, 1, 0, 'i', 4); +-- ORACLE> SELECT REGEXP_INSTR('1234567890 1234567890', '(123)(4(56)(78))', 1, 2, 0, 'i', 4) FROM DUAL; -> 18 +SELECT REGEXP_INSTR('1234567890 1234567890', '(123)(4(56)(78))', 1, 2, 0, 'i', 4); +-- ORACLE> SELECT REGEXP_INSTR('1234567890 1234567890', '(123)(4(56)(78))', 1, 2, 1, 'i', 4) FROM DUAL; -> 20 +SELECT REGEXP_INSTR('1234567890 1234567890', '(123)(4(56)(78))', 1, 2, 1, 'i', 4); +-- ORACLE> SELECT REGEXP_INSTR('1234567890 1234567890 1234567890', '(123)(4(56)(78))', 1, 3, 0,'i', 4) FROM DUAL; -> 29 +SELECT REGEXP_INSTR('1234567890 1234567890 1234567890', '(123)(4(56)(78))', 1, 3, 0, 'i', 4); +-- ORACLE> SELECT REGEXP_INSTR('1234567890 1234567890 1234567890', '(123)(4(56)(78))', 1, 3, 1,'i', 4) FROM DUAL; -> 31 +SELECT REGEXP_INSTR('1234567890 1234567890 1234567890', '(123)(4(56)(78))', 1, 3, 1, 'i', 4); +-- DROP TABLE regexp_temp; +-- CREATE TABLE regexp_temp(empName varchar2(20), emailID varchar2(20)); +-- INSERT INTO regexp_temp (empName, emailID) VALUES ('John Doe', 'johndoe@example.com'); +-- INSERT INTO regexp_temp (empName, emailID) VALUES ('Jane Doe', 'janedoe'); +-- COMMIT; +CREATE TEMPORARY TABLE regexp_temp(empName varchar(20), emailID varchar(20)); +INSERT INTO regexp_temp (empName, emailID) VALUES ('John Doe', 'johndoe@example.com'); +INSERT INTO regexp_temp (empName, emailID) VALUES ('Jane Doe', 'janedoe'); +-- -- ORACLE> SELECT emailID, REGEXP_INSTR(emailID, '\w+@\w+(\.\w+)+') "IS_A_VALID_EMAIL" FROM regexp_temp; +-- EMAILID IS_A_VALID_EMAIL +-- -------------------- ---------------- +-- johndoe@example.com 1 +-- example.com 0 +SELECT emailID, REGEXP_INSTR(emailID, '\w+@\w+(\.\w+)+') FROM regexp_temp; +-- -- ORACLE> SELECT emailID, REGEXP_INSTR(emailID, '\w+@\w+(\.\w+)+', 1, 1, 0, 'i', 0) "IS_A_VALID_EMAIL" FROM regexp_temp; +-- EMAILID IS_A_VALID_EMAIL +-- -------------------- ---------------- +-- johndoe@example.com 1 +-- example.com 0 +SELECT emailID, REGEXP_INSTR(emailID, '\w+@\w+(\.\w+)+', 1, 1, 0, 'i', 0) FROM regexp_temp; +-- -- ORACLE> SELECT emailID, REGEXP_INSTR(emailID, '\w+@\w+(\.\w+)+', 1, 1, 0, 'i', 1) "IS_A_VALID_EMAIL" FROM regexp_temp; +-- EMAILID IS_A_VALID_EMAIL +-- -------------------- ---------------- +-- johndoe@example.com 16 +-- example.com 0 +SELECT emailID, REGEXP_INSTR(emailID, '\w+@\w+(\.\w+)+', 1, 1, 0, 'i', 1) FROM regexp_temp; +DROP TABLE regexp_temp; +-- Check negatives values that must throw an error +SELECT REGEXP_INSTR('1234567890 1234567890 1234567890', '(123)(4(56)(78))', -1, 3, 1, 'i', 4); +SELECT REGEXP_INSTR('1234567890 1234567890 1234567890', '(123)(4(56)(78))', 1, -3, 1, 'i', 4); +SELECT REGEXP_INSTR('1234567890 1234567890 1234567890', '(123)(4(56)(78))', 1, 3, -1, 'i', 4); +SELECT REGEXP_INSTR('1234567890 1234567890 1234567890', '(123)(4(56)(78))', 1, 3, 1, 'i', -4); + + +---- +-- Tests for REGEXP_SUBSTR() +---- + +-- ORACLE> SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',[^,]+') FROM DUAL; -> , zipcode town +SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',[^,]+'); +-- ORACLE> SELECT REGEXP_SUBSTR('http://www.example.com/products', 'http://([[:alnum:]]+\.?){3,4}/?') FROM DUAL; -> http://www.example.com/ +SELECT REGEXP_SUBSTR('http://www.example.com/products', 'http://([[:alnum:]]+\.?){3,4}/?'); +-- ORACLE> SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',[^,]+', 24) FROM DUAL; -> , FR +SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',[^,]+', 24); +-- ORACLE> SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',[^,]+', 1, 1) FROM DUAL; -> , zipcode town +SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',[^,]+', 1, 1); +-- ORACLE> SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',[^,]+', 1, 2) FROM DUAL; -> , FR +SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',[^,]+', 1, 2); +-- ORACLE> SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',\s+[Zf][^,]+', 1, 1) FROM DUAL; -> NULL +SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',\s+[Zf][^,]+', 1, 1); +-- ORACLE> SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',\s+[Zf][^,]+', 1, 1, 'i') FROM DUAL; -> , zipcode town +SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',\s+[Zf][^,]+', 1, 1, 'i'); +-- ORACLE> SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',\s+[Zf][^,]+', 1, 1, 'i', 0) FROM DUAL; -> , zipcode town +SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',\s+[Zf][^,]+', 1, 1, 'i', 0); +-- ORACLE> SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',\s+[Zf][^,]+', 1, 1, 'i', 1) FROM DUAL; -> NULL +SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',\s+[Zf][^,]+', 1, 1, 'i', 1); +-- ORACLE> SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',\s+([Zf][^,]+)', 1, 1, 'i', 1) FROM DUAL; -> zipcode town +SELECT REGEXP_SUBSTR('number of your street, zipcode town, FR', ',\s+([Zf][^,]+)', 1, 1, 'i', 1); +-- ORACLE> SELECT REGEXP_SUBSTR('1234567890 1234567890', '(123)(4(56)(78))', 1, 1, 'i', 4) FROM DUAL; -> 78 +SELECT REGEXP_SUBSTR('1234567890 1234567890', '(123)(4(56)(78))', 1, 1, 'i', 4); +-- ORACLE> SELECT REGEXP_SUBSTR('1234567890 1234557890', '(123)(4(5[56])(78))', 1, 2, 'i', 3) FROM DUAL; -> 55 +SELECT REGEXP_SUBSTR('1234567890 1234557890', '(123)(4(5[56])(78))', 1, 2, 'i', 3); +-- ORACLE> SELECT REGEXP_SUBSTR('1234567890 1234567890', '(123)(4(56)(78))', 1, 1, 'i', 0) FROM DUAL; -> 12345678 +SELECT REGEXP_SUBSTR('1234567890 1234567890', '(123)(4(56)(78))', 1, 1, 'i', 0); +-- Check negatives values that must throw an error +SELECT REGEXP_SUBSTR('1234567890 1234567890', '(123)(4(56)(78))', -1, 1, 'i', 0); +SELECT REGEXP_SUBSTR('1234567890 1234567890', '(123)(4(56)(78))', 1, -1, 'i', 0); +SELECT REGEXP_SUBSTR('1234567890 1234567890', '(123)(4(56)(78))', 1, 1, 'i', -1); + +---- +-- Tests for REGEXP_REPLACE() +---- + +-- ORACLE> SELECT REGEXP_REPLACE('512.123.4567', '([[:digit:]]{3})\.([[:digit:]]{3})\.([[:digit:]]{4})', '(\1) \2-\3') FROM DUAL; -> (512) 123-4567 +SELECT REGEXP_REPLACE('512.123.4567', '([[:digit:]]{3})\.([[:digit:]]{3})\.([[:digit:]]{4})', '(\1) \2-\3'); +-- ORACLE> SELECT REGEXP_REPLACE('512.123.4567 612.123.4567', '([[:digit:]]{3})\.([[:digit:]]{3})\.([[:digit:]]{4})', '(\1) \2-\3') FROM DUAL; -> (512) 123-4567 (612) 123-4567 +SELECT oracle.REGEXP_REPLACE('512.123.4567 612.123.4567', '([[:digit:]]{3})\.([[:digit:]]{3})\.([[:digit:]]{4})', '(\1) \2-\3'); +-- ORACLE> SELECT REGEXP_REPLACE('number your street, zipcode town, FR', '( ){2,}', ' ') FROM DUAL; -> number your street, zipcode town, FR +SELECT oracle.REGEXP_REPLACE('number your street, zipcode town, FR', '( ){2,}', ' '); +-- ORACLE> SELECT REGEXP_REPLACE('number your street,'||CHR(10)||' zipcode town, FR', '( ){2,}', ' ') FROM DUAL; -> number your street, +-- zipcode town, FR +SELECT oracle.REGEXP_REPLACE('number your street,'||CHR(10)||' zipcode town, FR', '( ){2,}', ' '); +-- ORACLE> SELECT REGEXP_REPLACE('number your street, zipcode town, FR', '( ){2,}', ' ', 9) FROM DUAL; -> number your street, zipcode town, FR +SELECT oracle.REGEXP_REPLACE('number your street, zipcode town, FR', '( ){2,}', ' ', 9); +-- ORACLE> SELECT REGEXP_REPLACE('number your street, zipcode town, FR', '( ){2,}', ' ', 9, 0) FROM DUAL; -> number your street, zipcode town, FR +SELECT oracle.REGEXP_REPLACE('number your street, zipcode town, FR', '( ){2,}', ' ', 9, 0); +-- ORACLE> SELECT REGEXP_REPLACE('number your street, zipcode town, FR', '( ){2,}', ' ', 9, 2) FROM DUAL; -> number your street, zipcode town, FR +SELECT oracle.REGEXP_REPLACE('number your street, zipcode town, FR', '( ){2,}', ' ', 9, 2); +-- ORACLE> SELECT REGEXP_REPLACE('number your street, zipcode town, FR', '( ){2,}', ' ', 9, 2, 'm') FROM DUAL; -> number your street, zipcode town, FR +SELECT oracle.REGEXP_REPLACE('number your street, zipcode town, FR', '( ){2,}', ' ', 9, 2, 'm'); +-- ORACLE> SELECT REGEXP_REPLACE('number your street, zipcode town, FR', '([EURT]){2,}', '[\1]', 9, 2, 'i') FROM DUAL; -> number your s[t], zipcode town, FR +SELECT oracle.REGEXP_REPLACE('number your street, zipcode town, FR', '([EURT]){2,}', '[\1]', 9, 2, 'i'); +-- ORACLE> SELECT REGEXP_REPLACE('number your street, zipcode town, FR', '[ ]{2,}', ' ', 9, 2) FROM DUAL; -> number your street, zipcode town, FR +SELECT oracle.REGEXP_REPLACE('number your street, zipcode town, FR', '( ){2,}', ' ', 9, 2); + +-- ORACLE> SELECT REGEXP_REPLACE ('A PostgreSQL function', 'A|e|i|o|u', 'X', 1, 2) FROM DUAL; -> A PXstgreSQL function +SELECT oracle.REGEXP_REPLACE ('A PostgreSQL function', 'A|e|i|o|u', 'X', 1, 2); +-- ORACLE> SELECT REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', 1, 0, 'i') FROM DUAL; -> X PXstgrXSQL fXnctXXn +SELECT oracle.REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', 1, 0, 'i'); +-- ORACLE> SELECT REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', 1, 1, 'i') FROM DUAL; -> X PostgreSQL function +SELECT oracle.REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', 1, 1, 'i'); +-- ORACLE> SELECT REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', 1, 2, 'i') FROM DUAL; -> A PXstgreSQL function +SELECT oracle.REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', 1, 2, 'i'); +-- ORACLE> SELECT REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', 1, 3, 'i') FROM DUAL; -> A PostgrXSQL function +SELECT oracle.REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', 1, 3, 'i'); +-- ORACLE> SELECT REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', 1, 9, 'i') FROM DUAL; -> A PostgreSQL function +SELECT oracle.REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', 1, 9, 'i'); +-- ORACLE> SELECT REGEXP_REPLACE ('A PostgreSQL function', 'A|e|i|o|u', 'X', 1, 9) FROM DUAL; -> A PostgreSQL function +SELECT oracle.REGEXP_REPLACE ('A PostgreSQL function', 'A|e|i|o|u', 'X', 1, 9); +-- ORACLE> SELECT REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', -1, 0, 'i') FROM DUAL; -> ORA-01428 +SELECT oracle.REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', -1, 0, 'i'); +-- ORACLE> SELECT REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', 1, -1, 'i') FROM DUAL; -> ORA-01428 +SELECT oracle.REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', 1, -1, 'i'); +-- ORACLE> SELECT REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', 1, 1, 'g') FROM DUAL; -> ORA-01760 +SELECT oracle.REGEXP_REPLACE ('A PostgreSQL function', 'a|e|i|o|u', 'X', 1, 1, 'g'); + +-- +-- Test NULL input in the regexp_* functions that must returned NULL except for the modifier +-- or regexp flag. There is an exception with regexp_replace(), if the pattern is null (second +-- parameter) the original string is returned. We don't test functions witht the STRICT attribute +-- +SELECT oracle.REGEXP_LIKE(NULL, '\d+', 'i'); +SELECT oracle.REGEXP_LIKE('1234', NULL, 'i'); +SELECT oracle.REGEXP_LIKE('1234', '\d+', NULL); +SELECT oracle.REGEXP_LIKE('1234', '\d+', ''); +SELECT oracle.REGEXP_COUNT('1234', '\d', NULL) ; +SELECT oracle.REGEXP_COUNT('1234', '\d', 1, NULL) ; +SELECT oracle.REGEXP_COUNT('1234', '\d', 1, '') ; +SELECT oracle.REGEXP_COUNT('1234', '\d', NULL, NULL) ; +SELECT oracle.REGEXP_COUNT(NULL, '4', 1, 'i'); +SELECT oracle.REGEXP_INSTR('1234', '4', NULL); +SELECT oracle.REGEXP_INSTR('1234', '4', 1, NULL); +SELECT oracle.REGEXP_INSTR('1234', '4', 1, 1, NULL); +SELECT oracle.REGEXP_INSTR('1234', '4', 1, 1, 1, NULL); +SELECT oracle.REGEXP_INSTR('1234', '4', 1, 1, 0, NULL); +SELECT oracle.REGEXP_INSTR('1234', '4', 1, 1, 0, 'i', NULL); +SELECT oracle.REGEXP_INSTR('1234', '4', 1, 1, 0, '', NULL); +SELECT oracle.REGEXP_INSTR(NULL, '4', 1, 1, 0, 'i', 2); +SELECT oracle.REGEXP_INSTR(NULL, '4', 1, 1, 0, 'i', 2); +SELECT oracle.REGEXP_SUBSTR('1234', '1(.*)', null); +SELECT oracle.REGEXP_SUBSTR('1234', '234', 1, null); +SELECT oracle.REGEXP_SUBSTR('1234', '234', 1, 1, null); +SELECT oracle.REGEXP_SUBSTR('1234', '234', 1, 1, ''); +SELECT oracle.REGEXP_SUBSTR('1234', '234', 1, 1, 'i', null); +-- test for capture group +SELECT oracle.REGEXP_SUBSTR('1234', '2(3)(4)', 1, 1, 'i', 1); +SELECT oracle.REGEXP_SUBSTR('1234', '2(3)(4)', 1, 1, 'i', 2); +SELECT oracle.REGEXP_SUBSTR('1234', '2(3)(4)', 1, 1, 'i', 0); +-- Special case for second parameter in REGEXP_REPLACE, when null returns the original value. +SELECT oracle.REGEXP_REPLACE(null, '\d', 'a'); +SELECT oracle.REGEXP_REPLACE('1234', null, 'a'); +SELECT oracle.REGEXP_REPLACE('1234', null, null); +SELECT oracle.REGEXP_REPLACE('1234', '\d', null); +SELECT oracle.REGEXP_REPLACE('1234', '\d', 'a', null); +SELECT oracle.REGEXP_REPLACE('1234', null, 'a', 2); +SELECT oracle.REGEXP_REPLACE('1234', null, 'a', null); +SELECT oracle.REGEXP_REPLACE('1234', null, 'a', 1); +SELECT oracle.REGEXP_REPLACE('1234', null, 'a', 1, null); +SELECT oracle.REGEXP_REPLACE('1234', '\d', 'a', 1, null); +SELECT oracle.REGEXP_REPLACE('1234', '\d', 'a', 1, null); +SELECT oracle.REGEXP_REPLACE('1234', '\d', 'a', 1, null); +SELECT oracle.REGEXP_REPLACE('1234', '\d', 'a', 1, 1, null); +SELECT oracle.REGEXP_REPLACE('1234', '\d', 'a', 1, NULL, 'i'); +SELECT oracle.REGEXP_REPLACE('1234', null, 'a', 1, 1, 'i'); diff --git a/contrib/orafce/sql/varchar2.sql b/contrib/orafce/sql/varchar2.sql new file mode 100644 index 000000000..a496c5262 --- /dev/null +++ b/contrib/orafce/sql/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/contrib/orafce/sqlparse.c b/contrib/orafce/sqlparse.c new file mode 100644 index 000000000..bedd14efc --- /dev/null +++ b/contrib/orafce/sqlparse.c @@ -0,0 +1,1498 @@ +/* A Bison parser, made by GNU Bison 3.7.4. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 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. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +/* 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. */ + +/* Identify Bison output, and Bison version. */ +#define YYBISON 30704 + +/* Bison version string. */ +#define YYBISON_VERSION "3.7.4" + +/* 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 130 "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 + +#include "sqlparse.h" +/* Symbol kind. */ +enum yysymbol_kind_t +{ + YYSYMBOL_YYEMPTY = -2, + YYSYMBOL_YYEOF = 0, /* "end of file" */ + YYSYMBOL_YYerror = 1, /* error */ + YYSYMBOL_YYUNDEF = 2, /* "invalid token" */ + YYSYMBOL_X_IDENT = 3, /* X_IDENT */ + YYSYMBOL_X_NCONST = 4, /* X_NCONST */ + YYSYMBOL_X_SCONST = 5, /* X_SCONST */ + YYSYMBOL_X_OP = 6, /* X_OP */ + YYSYMBOL_X_PARAM = 7, /* X_PARAM */ + YYSYMBOL_X_COMMENT = 8, /* X_COMMENT */ + YYSYMBOL_X_WHITESPACE = 9, /* X_WHITESPACE */ + YYSYMBOL_X_KEYWORD = 10, /* X_KEYWORD */ + YYSYMBOL_X_OTHERS = 11, /* X_OTHERS */ + YYSYMBOL_X_TYPECAST = 12, /* X_TYPECAST */ + YYSYMBOL_YYACCEPT = 13, /* $accept */ + YYSYMBOL_root = 14, /* root */ + YYSYMBOL_elements = 15, /* elements */ + YYSYMBOL_anyelement = 16 /* anyelement */ +}; +typedef enum yysymbol_kind_t yysymbol_kind_t; + + + + +#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 + +/* 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 */ + +#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 + +/* YYMAXUTOK -- Last valid token kind. */ +#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 \ + ? YY_CAST (yysymbol_kind_t, yytranslate[YYX]) \ + : YYSYMBOL_YYUNDEF) + +/* 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 + +/** Accessing symbol of state STATE. */ +#define YY_ACCESSING_SYMBOL(State) YY_CAST (yysymbol_kind_t, yystos[State]) + +#if YYDEBUG || 0 +/* The user-facing name of the symbol whose (internal) number is + YYSYMBOL. No bounds checking. */ +static const char *yysymbol_name (yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED; + +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "\"end of file\"", "error", "\"invalid token\"", "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 +}; + +static const char * +yysymbol_name (yysymbol_kind_t yysymbol) +{ + return yytname[yysymbol]; +} +#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 +}; + + +enum { YYENOMEM = -2 }; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) + +#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) + +/* Backward compatibility with an undocumented macro. + Use YYerror or YYUNDEF. */ +#define YYERRCODE YYUNDEF + +/* 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 /* !defined YY_LOCATION_PRINT */ + + +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Kind, Value, Location, result); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, + yysymbol_kind_t yykind, 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 (yykind < YYNTOKENS) + YYPRINT (yyo, yytoknum[yykind], *yyvaluep); +# endif + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, List **result) +{ + YYFPRINTF (yyo, "%s %s (", + yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind)); + + YY_LOCATION_PRINT (yyo, *yylocationp); + YYFPRINTF (yyo, ": "); + yy_symbol_value_print (yyo, yykind, 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, + YY_ACCESSING_SYMBOL (+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) ((void) 0) +# define YY_SYMBOL_PRINT(Title, Kind, 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 + + + + + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, + yysymbol_kind_t yykind, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, List **result) +{ + YYUSE (yyvaluep); + YYUSE (yylocationp); + YYUSE (result); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/* Lookahead token kind. */ +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 = 0; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus = 0; + + /* Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* Their size. */ + YYPTRDIFF_T yystacksize = YYINITDEPTH; + + /* The state stack: array, bottom, top. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss = yyssa; + yy_state_t *yyssp = yyss; + + /* The semantic value stack: array, bottom, top. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp = yyvs; + + /* The location stack: array, bottom, top. */ + YYLTYPE yylsa[YYINITDEPTH]; + YYLTYPE *yyls = yylsa; + YYLTYPE *yylsp = yyls; + + int yyn; + /* The return value of yyparse. */ + int yyresult; + /* Lookahead symbol kind. */ + yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + YYLTYPE yyloc; + + /* The locations where the error started and ended. */ + YYLTYPE yyerror_range[3]; + + + +#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; + + YYDPRINTF ((stderr, "Starting parse\n")); + + 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 + YY_STACK_PRINT (yyss, yyssp); + + 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 empty, or end-of-input, or a valid lookahead. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token\n")); + yychar = yylex (); + } + + if (yychar <= YYEOF) + { + yychar = YYEOF; + yytoken = YYSYMBOL_YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else if (yychar == YYerror) + { + /* The scanner already issued an error message, process directly + to error recovery. But do not keep the error token as + lookahead, it is too special and may lead us to an endless + loop in error recovery. */ + yychar = YYUNDEF; + yytoken = YYSYMBOL_YYerror; + yyerror_range[1] = yylloc; + goto yyerrlab1; + } + 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: /* root: elements */ +#line 91 "sqlparse.y" + { *((void**)result) = (yyvsp[0].list); } +#line 1225 "sqlparse.c" + break; + + case 3: /* elements: anyelement */ +#line 95 "sqlparse.y" + { (yyval.list) = list_make1((yyvsp[0].node));} +#line 1231 "sqlparse.c" + break; + + case 4: /* elements: elements anyelement */ +#line 96 "sqlparse.y" + { (yyval.list) = lappend((yyvsp[-1].list), (yyvsp[0].node));} +#line 1237 "sqlparse.c" + break; + + case 5: /* anyelement: X_IDENT */ +#line 100 "sqlparse.y" + { (yyval.node) = (orafce_lexnode*) CREATE_NODE((yyvsp[0].val), IDENT); } +#line 1243 "sqlparse.c" + break; + + case 6: /* anyelement: X_NCONST */ +#line 101 "sqlparse.y" + { (yyval.node) = (orafce_lexnode*) CREATE_NODE((yyvsp[0].val), NCONST); } +#line 1249 "sqlparse.c" + break; + + case 7: /* anyelement: X_SCONST */ +#line 102 "sqlparse.y" + { (yyval.node) = (orafce_lexnode*) CREATE_NODE((yyvsp[0].val), SCONST); } +#line 1255 "sqlparse.c" + break; + + case 8: /* anyelement: X_OP */ +#line 103 "sqlparse.y" + { (yyval.node) = (orafce_lexnode*) CREATE_NODE((yyvsp[0].val), OP); } +#line 1261 "sqlparse.c" + break; + + case 9: /* anyelement: X_PARAM */ +#line 104 "sqlparse.y" + { (yyval.node) = (orafce_lexnode*) CREATE_NODE((yyvsp[0].val), PARAM); } +#line 1267 "sqlparse.c" + break; + + case 10: /* anyelement: X_COMMENT */ +#line 105 "sqlparse.y" + { (yyval.node) = (orafce_lexnode*) CREATE_NODE((yyvsp[0].val), COMMENT); } +#line 1273 "sqlparse.c" + break; + + case 11: /* anyelement: X_WHITESPACE */ +#line 106 "sqlparse.y" + { (yyval.node) = (orafce_lexnode*) CREATE_NODE((yyvsp[0].val), WHITESPACE); } +#line 1279 "sqlparse.c" + break; + + case 12: /* anyelement: X_KEYWORD */ +#line 107 "sqlparse.y" + { (yyval.node) = (orafce_lexnode*) CREATE_NODE((yyvsp[0].val), KEYWORD); } +#line 1285 "sqlparse.c" + break; + + case 13: /* anyelement: X_OTHERS */ +#line 108 "sqlparse.y" + { (yyval.node) = (orafce_lexnode*) CREATE_NODE((yyvsp[0].val), OTHERS); } +#line 1291 "sqlparse.c" + break; + + +#line 1295 "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 ("-> $$ =", YY_CAST (yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + + *++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 ? YYSYMBOL_YYEMPTY : YYTRANSLATE (yychar); + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; + yyerror (result, YY_("syntax error")); + } + + 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. */ + + /* Pop stack until we find a state that shifts the error token. */ + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYSYMBOL_YYerror; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror) + { + 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", + YY_ACCESSING_SYMBOL (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; + ++yylsp; + YYLLOC_DEFAULT (*yylsp, yyerror_range, 2); + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", YY_ACCESSING_SYMBOL (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 +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (result, YY_("memory exhausted")); + yyresult = 2; + goto yyreturn; +#endif + + +/*-------------------------------------------------------. +| yyreturn -- parsing is finished, clean up and return. | +`-------------------------------------------------------*/ +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", + YY_ACCESSING_SYMBOL (+*yyssp), yyvsp, yylsp, result); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + + return yyresult; +} + +#line 110 "sqlparse.y" + + +#undef YYLTYPE + +#include "sqlscan.c" diff --git a/contrib/orafce/sqlparse.h b/contrib/orafce/sqlparse.h new file mode 100644 index 000000000..ca1b993f3 --- /dev/null +++ b/contrib/orafce/sqlparse.h @@ -0,0 +1,116 @@ +/* A Bison parser, made by GNU Bison 3.7.4. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 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. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +#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 kinds. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + YYEMPTY = -2, + YYEOF = 0, /* "end of file" */ + YYerror = 256, /* error */ + YYUNDEF = 257, /* "invalid token" */ + X_IDENT = 258, /* X_IDENT */ + X_NCONST = 259, /* X_NCONST */ + X_SCONST = 260, /* X_SCONST */ + X_OP = 261, /* X_OP */ + X_PARAM = 262, /* X_PARAM */ + X_COMMENT = 263, /* X_COMMENT */ + X_WHITESPACE = 264, /* X_WHITESPACE */ + X_KEYWORD = 265, /* X_KEYWORD */ + X_OTHERS = 266, /* X_OTHERS */ + X_TYPECAST = 267 /* X_TYPECAST */ + }; + typedef enum yytokentype yytoken_kind_t; +#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 90 "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/contrib/orafce/sqlparse.y b/contrib/orafce/sqlparse.y new file mode 100644 index 000000000..52e6f3740 --- /dev/null +++ b/contrib/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/contrib/orafce/sqlscan.c b/contrib/orafce/sqlscan.c new file mode 100644 index 000000000..4483e3eb3 --- /dev/null +++ b/contrib/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/contrib/orafce/sqlscan.l b/contrib/orafce/sqlscan.l new file mode 100644 index 000000000..919346e42 --- /dev/null +++ b/contrib/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/contrib/orafce/utility.c b/contrib/orafce/utility.c new file mode 100644 index 000000000..65875953e --- /dev/null +++ b/contrib/orafce/utility.c @@ -0,0 +1,227 @@ +/* + 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-2021 + + This module is under BSD Licence + + History: + 1.0. first public version 22. September 2006 + +*/ + +#include +#include + +#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); +PG_FUNCTION_INFO_V1(dbms_utility_get_time); + +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))); +} + +/* + * Returns the number of hundredths of seconds that have elapsed + * since a point in time in the past. + */ +Datum +dbms_utility_get_time(PG_FUNCTION_ARGS) +{ + struct timeval tv; + + gettimeofday(&tv,NULL); + PG_RETURN_INT32((int32) (tv.tv_sec*1000000+tv.tv_usec)/10000); +} diff --git a/contrib/orafce/varchar2.c b/contrib/orafce/varchar2.c new file mode 100644 index 000000000..cd155d315 --- /dev/null +++ b/contrib/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); +} -- Gitee