Orafce — Oracle-compatible functions and packages

This QHB extension implements functions and operators that emulate a subset of functions and packages from the Oracle RDBMS.

Note that this orafce version works with QHB version 1.5.1 and later.


Installation

Oracle package and compatibility functions for QHB are available as package qhb-1.5.3-orafce.

Add QHB package repository and install the extension package for chosen platform from the download page.


Oracle Functions and Packages

This module contains some useful functions that can help with porting Oracle application to QHB 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 1582-10-05 with the 'J' format and before 1100-03-01 with other formats cannot be verified due to a bug in Oracle.

All functions are fully compatibles with Oracle and respect all known format strings.


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.

Example:

add_months(date '2005-05-31',1) → 2005-06-30

last_day ( date ) → date

Returns last day of the month based on a date value.

Example:

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.

Example:

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..Saturday.

Example:

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.

Example:

months_between(date '1995-02-02', date '1995-01-01') → 1.0322580645161

trunc ( date, text ) → date

Truncates date according to the specified format.

Example:

trunc(date '2005-07-12', 'iw') → 2005-07-11

round ( date, text ) → date

Rounds dates according to the specified format.

Example:

round(date '2005-07-12', 'yyyy') → 2006-01-01

to_date ( text ) → timestamp

Typecasts 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 QHB’s datestyle GUC setting.

Examples:

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

to_date (text, text) → timestamp

Typecasts input text with the specified format to timestamp. The GUC orafce.orafce_emit_error_on_date_bug is used to specify whether an error is reported when the date value hits the Oracle bug on dates. This bug appears with dates before 1582-10-05 when the 'J' format is used ('J2299159') and before 1100-03-01 with other formats. An error is reported by default, to disable this behavior set orafce.orafce_emit_error_on_date_bug to off.

SELECT oracle.to_date('112012', 'J');
ERROR:  Dates before 1582-10-05 ('J2299159') cannot be verified due to a bug in Oracle.

SELECT oracle.to_date('1003-03-15', 'yyyy-mm-dd');
ERROR:  Dates before 1100-03-01 cannot be verified due to a bug in Oracle.

SET orafce.oracle_compatibility_date_limit TO off;

SELECT oracle.to_date('112012', 'J');
	to_date
------------------------
 4407-07-30 00:00:00 BC
(1 row)

SELECT oracle.to_date('1003/03/15', 'yyyy/mm/dd');
       to_date
---------------------
 1003-03-15 00:00:00
(1 row)

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 ) → timestamp with time zone

Returns date and time plus n months

Example:

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 ) → timestamp with time zone

Returns last day of the month based on a date value.

Example:

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 ) → timestamp with time zone

Returns the first weekday that is greater than a date value.

Example:

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 ) → timestamp with time zone

Same as above. The second argument should be 1..7 and interpreted as Sunday..Saturday.

Example:

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 ) → numeric

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.

Example:

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 ) → timestamp without time zone

Returns timestamp without time zone.

Example:

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 ) → oracle.date

Returns oracle.date.

Example:

oracle.to_date('16.02.09 04:12:12') → 2009-02-16 04:12:12

oracle.sysdate() → timestamp with time zone

Returns statement timestamp at server timezone (orafce.timezone).

Example:

oracle.sysdate() → 2015-12-09 17:47:56

oracle.dbtimezone() → timezone

Returns server time zone - emulated via orafce.timezone.

Example:

oracle.dbtimezone() → GMT

oracle.sessiontimezone() → timezone

Returns session timezone — current QHB timezone.

Example:

oracle.sessiontimezone() → Europe/Prague

oracle.sys_extract_utc ( timestamp with timezone ) → timestamp with time zone

Returns timestamp in UTC timezone.

Example:

oracle.sys_extract_utc(current_timestamp)

oracle.sys_extract_utc ( oracle.date ) → timestamp with time zone

Returns timestamp in UTC timezone, when time zone is not specified, session (current QHB) timezone is used.

Example:

oracle.sys_extract_utc(oracle.date '2005-05-24 11:21:12', 1) -> 2005-05-24 09:21:12

oracle.to_char ( timestamp ) → nls_date_format

Returns timestamp in nls_date_format.

Examples:

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 ) → oracle.date

Returns oracle.date.

Example:

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 ) → oracle.date

Returns oracle.date.

Example:

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 ) → oracle.date

Returns oracle.date.

Example:

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 ) → oracle.date

Returns oracle.date.

Example:

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 ) → oracle.date

Returns oracle.date.

Example:

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 ) → oracle.date

Returns oracle.date.

Example:

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 ) → oracle.date

Returns oracle.date.

Example:

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 ) → oracle.date

Returns oracle.date.

Example:

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 ) → double precision

Returns double precision.

Example:

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, and oracle.months_between are installed side-by-side with pg_catalog.add_months, pg_catalog.last_day, pg_catalog.next_day, and pg_catalog.months_between.


Table Dual

QHB does not need Oracle’s table 'dual', but since it is intensively used by Oracle users, it has been added in orafce. This table is in schema oracle. Usually you want allow unqualified access — so you should to add this schema to search_path configuration (like search_path = 'oracle, pg_catalog, "$user", public' in qhb.conf).


Package dbms_output

QHB 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)

Closes file.

utl_file.fclose_all()

Closes all files.

utl_file.fcopy (_ src_location_ text, src_filename text, dest_location text, dest_filename text [, start_line text ] [, end_line text ] )

Copies text file.

utl_file.fflush ( file utl_file.file_type )

Flushes all data from buffers.

utl_file.fgetattr ( location text, filename text )

Gets file attributes.

utl_file.fopen ( location text, filename text, file_mode text [, maxlinesize int ] [, encoding name ] ) → utl_file.file_type

Opens file.

utl_file.fremove ( location text, filename text )

Removes file.

utl_file.frename ( location text, filename text, dest_dir text, dest_file text [, overwrite bool ] )

Renames file.

utl_file.get_line ( file utl_file.file_type) → text

Reads one line from file.

utl_file.get_nextline ( file utl_file.file_type) → text

Reads 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, buffer text, format [, arg1 text ] [, arg2 text ] [..] [, arg5 text ] )

Puts formatted text into file.

utl_file.tmpdir()

Gets path of temp directory.

Because QHB doesn’t support call by reference, some functions are slightly different: fclose and get_line.

DO $$
DECLARE f utl_file.file_type;
BEGIN
  f := utl_file.fopen('/tmp', 'sample.txt', 'r');
  <<readl>>
  loop
    BEGIN
      raise notice '%', utl_file.get_line(f);
    exception
      WHEN no_data_found THEN
        exit readl;
    END;
  end loop;
  f := utl_file.fclose(f);
END;
$$;

or second (with QHB specific function get_nextline)

DO $$
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;
  end loop
exception
  WHEN others THEN
  perform 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 \ every time.

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 QHB) 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 (superuser). 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_sql

This is implementation of Oracle’s API of package DBMS_SQL.

It doesn’t ensure full compatibility, but should to decrease a work necessary for successful migration.

Attention: QHB architecture is different than Oracle’s architecture. PL/pgSQL is executed in same context like SQL engine. Then is not any reason to use Oracle’s patterns like bulk collect and iteration over collection in QHB to get good performance. This code is designed to reduce work related to porting some applications from Oracle to QHB, and it can work well. But there will not be any performance advantage against build-in PL/pgSQL statements. The emulation of Oracle’s API has memory and CPU overhead, that can be significant on bigger data.


Functionality

This extension implements subset of Oracle’s dbms_sql interface. The goal of this extension is not a compatibility with Oracle, it is designed to reduce some work related migration Oracle’s applications to QHB. Some basic bulk DML functionality is supported:

DO $$
DECLARE
  c int;
  a int[];
  b varchar[];
  ca numeric[];
BEGIN
  c := dbms_sql.open_cursor();
  call dbms_sql.parse(c, 'insert into foo values(:a, :b, :c)');
  a := ARRAY[1, 2, 3, 4, 5];
  b := ARRAY['Ahoj', 'Nazdar', 'Bazar'];
  ca := ARRAY[3.14, 2.22, 3.8, 4];

  call dbms_sql.bind_array(c, 'a', a, 2, 3);
  call dbms_sql.bind_array(c, 'b', b, 3, 4);
  call dbms_sql.bind_array(c, 'c', ca);
  raise notice 'inserted rows %d', dbms_sql.execute(c);
END;
$$;

DO $$
DECLARE
  c int;
  a int[];
  b varchar[];
  ca numeric[];
BEGIN
  c := dbms_sql.open_cursor();
  call dbms_sql.parse(c, 'select i, ''Ahoj'' || i, i + 0.003 from generate_series(1, 35) g(i)');
  call dbms_sql.define_array(c, 1, a, 10, 1);
  call dbms_sql.define_array(c, 2, b, 10, 1);
  call dbms_sql.define_array(c, 3, ca, 10, 1);

  perform dbms_sql.execute(c);
  while dbms_sql.fetch_rows(c) > 0
  loop
    call dbms_sql.column_value(c, 1, a);
    call dbms_sql.column_value(c, 2, b);
    call dbms_sql.column_value(c, 3, ca);
    raise notice 'a = %', a;
    raise notice 'b = %', b;
    raise notice 'c = %', ca;
  end loop;
  call dbms_sql.close_cursor(c);
END;
$$;

There is function dbms_sql.describe_columns_f, that is like procedure dbms_sql.describe_columns. Attention, the type IDs are related to QHB type system. The values are not converted to Oracle’s numbers.

DO $$
DECLARE
  c int;
  r record;
  d dbms_sql.desc_rec;
BEGIN
  c := dbms_sql.open_cursor();
  call dbms_sql.parse(c, 'select * from pg_class');
  r := dbms_sql.describe_columns(c);
  raise notice '%', r.col_cnt;

  foreach d in array r.desc_t
  loop
    raise notice '% %', d.col_name, d.col_type::regtype;
  end loop;

  call dbms_sql.close_cursor(c);
END;
$$;

DO $$
DECLARE
  c int;
  n int;
  d dbms_sql.desc_rec;
  da dbms_sql.desc_rec[];
BEGIN
  c := dbms_sql.open_cursor();
  call dbms_sql.parse(c, 'select * from pg_class');
  call dbms_sql.describe_columns(c, n, da);
  raise notice '%', n;

  foreach d in array da
  loop
    raise notice '% %', d.col_name, d.col_type::regtype;
  end loop;

  call dbms_sql.close_cursor(c);
END;
$$;

Package dbms_pipe

This package is an emulation of dbms_pipe Oracle package. It provides inter-session communication. 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)
  • QHB 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

Gets the date created by adding n business days to a date.

plvdate.nearest_bizday ( day date ) → date

Gets the nearest business date to a given date, user defined.

plvdate.next_bizday ( day date ) → date

Gets the next business date from a given date, user defined.

plvdate.bizdays_between ( day1 date, day2 date ) → int

Gets the number of business days between two dates.

plvdate.prev_bizday ( day date ) → date

Gets the previous business date from a given date.

plvdate_isbizday ( date ) → bool

Returns true if a date is a business day.

plvdate.set_nonbizday ( dow varchar )

Sets day of week as non business day.

plvdate.unset_nonbizday ( dow varchar )

Unsets day of week as non business day.

plvdate.set_nonbizday ( day date )

Sets day as non business day.

plvdate.unset_nonbizday ( day date )

Unsets day as non business day.

plvdate.set_nonbizday ( day date, repeat bool )

Sets day as non business day, if repeat is true, then day is nonbizday every year.

plvdate.unset_nonbizday ( day date, repeat bool )

Unsets day as non business day, if repeat is true, then day is nonbizday every year

plvdate.use_easter()

Easter Sunday and Easter Monday will be holidays.

plvdate.unuse_easter()

Easter Sunday and Easter Monday will not be holidays.

plvdate.use_easter ( useit bool )

If true then Easter Sunday and Easter Monday will be holidays.

plvdate.using_easter() → bool

Returns true if Easter Sunday and Easter Monday are holidays.

plvdate.use_great_friday()

Easter Great Friday will be holiday.

plvdate.unuse_easter()

Easter Great Friday will not be holiday.

plvdate.use_easter ( useit bool )

If true then Easter Great Friday will be holiday.

plvdate.using_easter() → bool

Returns true if Easter Great Friday is holiday.

plvdate.include_start()

Include starting date in bizdays_between calculation.

plvdate.noinclude_start()

Exclude starting date from bizdays_between calculation.

plvdate.include_start ( include bool )

If true threading starting date will be included in bizdays_between calculation.

plvdate.include_start() → bool

Returns true if starting date is included in bizdays_between calculation.

plvdate.default_holidays ( varchar )

Loads 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:

qhb=# select plvdate.default_holidays('czech');
 default_holidays
 -----------------

(1 row)
qhb=# 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 )

Normalizes string — replaces white chars by space, replaces spaces by space.

plvstr.is_prefix ( str text, prefix text, cs bool ) → bool

Returns true, if prefix is prefix of string.

plvstr.is_prefix ( str text, prefix text ) → bool

Returns true, if prefix is prefix of string.

plvstr.is_prefix ( str int, prefix int ) → bool

Returns true, if prefix is prefix of string.

plvstr.is_prefix ( str bigint, prefix bigint ) → bool

Returns true, if prefix is prefix of string.

plvstr.substr ( str text, start int, len int ) → text

Returns the substring of string starting at the start'th character and stopping after len characters.

plvstr.substr ( str text, start int ) → text

Returns substring of string starting at the start'th character and extending to end of the string.

plvstr.instr ( str text, patt text, start int, n int ) → text

Search nth pattern in string starting at the start'th character.

plvstr.instr ( str text, patt text, start int ) → text

Search pattern in string starting at the start'th character.

plvstr.instr ( str text, patt text ) → text

Search pattern in string.

plvstr.lpart ( str text, div text, start int, n int, all_if_notfound bool ) → text

Returns the left part of a string. When all_if_notfound is true, then if specified item is missing, the entire string is returned.

plvstr.lpart ( str text, div text, start int, n int ) → text

Returns the left part of a string.

plvstr.lpart ( str text, div text, start int ) → text

Returns the left part of a string.

plvstr.lpart ( str text, div text ) → text

Returns the left part of a string.

plvstr.rpart ( str text, div text, start int, n int, all_if_notfound bool ) → text

Returns the right part of a string. When all_if_notfound is true, then if specified item is missing, the entire string is returned.

plvstr.rpart ( str text, div text, start int, n int ) → text

Returns the right part of a string.

plvstr.rpart ( str text, div text, start int ) → text

Returns the right part of a string.

plvstr.rpart ( str text, div text ) → text

Returns the right part of a string.

plvstr.lstrip ( str text, substr text, num int ) → text

Removes characters from the beginning.

plvstr.lstrip ( str text, substr text ) → text

Removes characters from the beginning.

plvstr.rstrip ( str text, substr text, num int ) → text

Removes characters from the end.

plvstr.rstrip ( str text, substr text ) → text

Removes characters from the end.

plvstr.rvrs ( str text, start int, end int ) → text

Reverses string or part of string.

plvstr.rvrs ( str text, start int ) → text

Reverses string or part of string.

plvstr.rvrs ( str text ) → text

Reverses string or part of string.

plvstr.left ( str text, n int ) → text

Returns first n characters. You can use negative n.

plvstr.right ( str text, n int ) → text

Returns last n characters. You can use negative n.

plvstr.swap ( str text, replace text, start int, length int ) → text

Replaces the substring of string str that starts at the start'th character and extends for length characters with replace.

plvstr.swap ( str text, replace text ) → text

Replaces the substring of string str with replace.

plvstr.betwn ( str text, start int, end int, inclusive bool ) → text

Finds the substring between start and end locations. If inclusive is true, boundary characters are included in the search.

plvstr.betwn ( str text, start int, end int, startnth int, endnth int, inclusive bool, gotoend bool ) → text

Finds the substring between start and end locations. If inclusive is true, boundary characters are included in the search. If gotoend is true, search continues to the end of the string.

plvstr.betwn ( str text, start int, end int ) → text

Finds the substring between start and end locations.

plvstr.betwn ( str text, start int, end int, startnth int, endnth int ) → text

Finds the substring between start and end locations.

plvchr.nth ( str text, n int ) → text

Returns the nth character in a string.

plvchr.first ( str text ) → text

Returns the first character in a string.

plvchr.last ( str text ) → text

Returns the last character in a string.

plvchr.is_blank ( c int )

It is blank.

plvchr.is_blank ( c text )

It is blank.

plvchr.is_digit ( c int )

It is digit.

plvchr.is_digit ( c text )

It is digit.

plvchr.is_quote ( c int )

It is quote.

plvchr.is_quote ( c text )

It is quote.

plvchr.is_other ( c int )

It is other.

plvchr.is_other ( c text )

It is other.

plvchr.is_letter ( c int )

It is letter.

plvchr.is_letter ( c text )

It 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 int )

Quoted text between ".

plvchr.stripped ( str text, char 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 text, vals 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 text, vals text[], subst 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 text, vals text[], delim 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 text, vals text[], delim text subst text )

Scans a string for all instances of the substitution keyword and replace it with the next value in the substitution values list.

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']);
          _`строка`_
 --------------------------
 My name is Pavel Stěhule.
(1 row)

SELECT plvsubst.string('My name is %s %s.', 'Pavel,Stěhule');
          _`строка`_
 --------------------------
 My name is Pavel Stěhule.
(1 row)

SELECT plvsubst.string('My name is $$ $$.', 'Pavel|Stěhule','|','$$');
          _`строка`_
 --------------------------
 My name is Pavel Stěhule.
(1 row)

Package DBMS_utility

dms_utility.format_call_stack()

Returns a formatted string with content of call stack.

qhb=# 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.

plvlex.tokens ( str text, skip_spaces bool, qualified_names bool )

Returns table of lexical elements in string.

qhb=# 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 QHB versions!


Package DBMS_ASSERT

This package protects user input against SQL injection.

dbms_assert.enquote_literal ( varchar ) → varchar

Adds leading and trailing quotes, verify that all single quotes are paired with adjacent single quotes.

dbms_assert.enquote_name ( varchar [, boolean ] ) → varchar

Encloses name in double quotes. Optional second parameter ensure lowering 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

This 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

This function verifies that input string is qualified SQL identifier of an existing SQL object.


Package 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)

Initializes 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)

Returns seed value.

dbms_random.seed(text)

Resets seed value.

dbms_random.string(opt text(1), _len_ int)

Creates random string.

dbms_random.terminate()

Terminates package (does nothing in QHB).

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, greatest, least, bitand, nanvl, sinh, cosh, tanh, oracle.substr, and oracle.mod.

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 ( text [, characters text ] )

Oracle compatible btrim.

oracle.length ( string char )

Oracle compatible length.

oracle.listagg ( str text [, separator text ] )

Aggregates values to list.

oracle.wm_concat ( str text )

Aggregates values to comma separated list.

oracle.median(float4)

Calculates a median.

oracle.median(float8)

Calculates a median.

oracle.to_number(text)

Converts a string to a number.

oracle.to_number(numeric)

Converts a string to a number.

oracle.to_number(numeric,numeric)

Converts a string to a number.

public.to_multi_byte(text)

Converts all single-byte characters to their corresponding multibyte characters.

public.to_single_byte(text)

Converts all multi-byte characters to their corresponding single-byte characters.

oracle.greatest(anyelement, anyelement[])

Oracle compatibility greatest, return NULL on NULL input.

oracle.least(anyelement, anyelement[])

Oracle compatibility least, return NULL on NULL input.

oracle.mod(int, int)

Oracle compatibility mod; If the second parameter is zero, it returns the first parameter.

oracle.remainder(int, int)

Returns remainder of number divided by another number.

oracle.remainder(numeric, numeric)

Returns remainder of number divided by another number.

oracle.sys_guid() → bytea

Returns bytea - 16 bytes of global uniq ID.

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, and 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, and pg_catalog.length respectively.

Functions oracle.decode, oracle.greatest and oracle.least must always be prefixed by the schema name even if the oracle is before pg_catalog in the search_path because these functions are implemented inside QHB parser and analyzer. Without the schema name the internal functions will always be used.

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 QHB char type only supports character semantics.

The oracle.substr with three arguments can returns different result (null or empty string) in dependency to setting orafce.using_substring_zero_width_in_substr (oracle, warning_oracle, orafce, warning_orafce). This different result is returned only when third argument (substring_length) is zero. Default is warning_oracle, that means raising warning and returning null.


oracle.sys_guid() Function

This functions returns global unique id. It calls specified functions from uuid-ossp extension, and then this function should be installed before function sys_guid is used. By default this function uses function uuid_generate_v1, but function uuid_generate_v1mc, uuid_generate_v4 can be used too (by setting orafce.sys_guid_source). oracle.sys_guid can use build-in gen_random_uuid func too. In this case the extension uuid-ossp is not required.


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 QHB 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 behaviour is very similar to Oracle.

WARNING!
When result is empty string, then result is NULL. This behaviour is disabled by default.

WARNING!
There is possible incompatibility between 3.7 and older Orafce releases. An 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 QHB 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 QHB 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])

Extracts a substring of specified length (in bytes) starting at a given byte position (counting from one); if the third argument is not 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 QHB NULL and empty string are different values. For simplicity is good to ensure (in QHB 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 ensured with trigger functions:

oracle.replace_empty_strings([raise_warnings boolean])
oracle.replace_null_strings([raise_warnings boolean])

Optional string argument is used as indicator so these functions should to raise warning (possibly error) 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_component_version
  • oracle.user_objects
  • oracle.dba_segments

See Also

orafce documentation on Github