¾�ΥС�������ʸ�� �� 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9.6 | 9.5 | 9.4 | 9.3 | 9.2 | 9.1 | 9.0 | 8.4 | 8.3 | 8.2 | 8.1 | 8.0 | 7.4 | 7.3 | 7.2

19.11. Oracle PL/SQL ����ΰܿ�

����: Roberto Mello ()

������Ǥϳ�ȯ�Ԥإ��饯�뤫�� PostgreSQL �ؤΥ��ץꥱ�������ΰܿ��μ�����Ȥʤ�褦���饯��� PL/SQL �� PostgreSQL �� PL/pgSQL �ΰ㤤�ˤĤ����������ޤ��������ˤ��륳���ɤ�����ʬ��ArsDigita Clickstream module�ˤ���ޤ��� ���2000ǯ�βƤ�OpenForce Inc.�ǥ��󥿡���½���Ԥä����� PostgreSQL �˰ܿ����ޤ�����

PL/pgSQL �� ¿�������� PL/SQL �˻��Ƥ��ޤ�������ϥ֥��å��ǹ�������Ƥ��ơ����ʤʸ���Ǥ���Ȥ������ȤǤ��� (���Ƥ��ѿ����������ʤ���Ф����ޤ���)PL/SQL �� PostgreSQL ���б������Τ��¿���ε�ǽ������ޤ��������� PL/pgSQL �ˤ�¿���ε�ǽ�����ꡢ������䤨����������Ƥ��ޤ���

19.11.1. ��ʰ㤤

Oracle ���� PostgreSQL �˰ܿ�����Ȥ����դ��٤����������Ĥ�����ޤ���

19.11.1.1. ñ�������Υ���������

PostgreSQL �Ǥϡ��ؿ������Ǥ�ñ�������򥨥������פ�����ɬ�פ�����ޤ���Example 19-6 �Τ褦��¾�δؿ�����������褦�ʴؿ��������������ä˳ڤ��������ɤ�񤯤��Ȥ��Ǥ��ޤ������������ñ�������򥨥������פ���ݤ����դ򤷤Ƥۤ����Τϡ��ǽ�ȺǸ�ΰ���������������ʳ��Τ��٤Ƥ϶����ˤʤ뤳�ȤǤ���

Table 19-1 �ϥ������פǤ���(���ξ����ʿޤ򤭤äȵ������äƤ���뤳�ȤǤ��礦)

Table 19-1. ���󥰥륯�����ȤΥ��������׿�

�ֹ��Ȥ��������
1�ؿ����Τκǽ�ȺǸ�
CREATE FUNCTION foo() RETURNS INTEGER AS '...'
LANGUAGE 'plpgsql';
���Τޤ�
2������ SELECT ʸ���ʸ����ʤɤ�Ϥ���
a_output := ''Blah'';
SELECT * FROM users WHERE f_name=''foobar'';
SELECT * FROM users WHERE f_name='foobar';
4ʸ���������Ƿ�̤�ʸ�������Ĥ�ñ������䤬ɬ�פʻ�
a_output := a_output || '' AND name
    LIKE ''''foobar'''' AND ...''
AND name LIKE 'foobar' AND ...
6��̤Ȥ���������ʸ�������Ű����䤬�ߤ��������������ʸ�����λ���������
a_output := a_output || '' AND name
    LIKE ''''foobar''''''
AND name LIKE 'foobar'
10��̤�ʸ�������Ĥ�ñ������䤬�ߤ������(8�Ĥΰ����䤬ɬ��) �����Ϥ���ʸ�����λ�����������(�⤦ 2 ��)��Example 19-6�Τ褦��)�ؿ���Ȥä�¾�δؿ���������������ˤ���ɬ�פǤ��礦��
a_output := a_output || '' if v_'' ||
    referrer_keys.kind || '' like '''''''''' 
    || referrer_keys.key_string || '''''''''' 
    then return ''''''  || referrer_keys.referrer_type 
    || ''''''; end if;''; 
if v_<...> like ''<...>'' then return ''<...>''; end if;

19.11.2. �ؿ��ΰܿ�

Example 19-5. ��ñ�ʴؿ�

����ϥ��饯��δؿ��Ǥ���

CREATE OR REPLACE FUNCTION cs_fmt_browser_version(v_name IN varchar, v_version IN varchar)
RETURN varchar IS
BEGIN
    IF v_version IS NULL THEN
        RETURN v_name;
    END IF;
    RETURN v_name || '/' || v_version;
END;
/
SHOW ERRORS;

���δؿ����̤��ơ�PL/pgSQL �Ȥΰ㤤�򸫤Ƥߤޤ��礦��

  • PostgreSQL �ˤ�̾���դ��ѥ�᡼���Ϥ���ޤ��󡣴ؿ�����������Ū����̾���դ���ɬ�פ�����ޤ���

  • Oracle �� IN��OUT�� INOUT �Ȥ����ѥ�᡼����ؿ����Ϥ����Ȥ��Ǥ��ޤ��� �㤨�С�INOUT �ϡ��ѥ�᡼�����ͤ������ä��̤��ͤ��֤����Ȥ��̣���ޤ���PostgreSQL �Ǥ� "IN" �ѥ�᡼�������ʤ����ᡢ�ؿ���ñ����ͤ����֤��ޤ���

  • �ؿ��ץ��ȥ�������� RETURN �������(�ؿ����ΤǤϤ���ޤ���)�� PostgreSQL �Ǥ� RETURNS �ˤʤ�ޤ���

  • PostgreSQL �ؿ��Ǥ�ñ������䤬���ڤ�ʸ���Ȥ��ƻ��Ѥ���Ƥ���Τǡ��ؿ���Ǥ�ñ�������򥨥������פ��ʤ���Ф����ޤ���(���줬�������餤�餵�����Ǥ��� Section 19.11.1.1�򻲾Ȥ��Ƥ�������)��

  • PostgreSQL �ˤ�/show errors ���ޥ�ɤϤ���ޤ���

����ǤϤ��δؿ��� PostgreSQL �˰ܿ������ȤɤΤ褦�ˤʤ뤫�ߤƤߤޤ��礦��

CREATE OR REPLACE FUNCTION cs_fmt_browser_version(VARCHAR, VARCHAR)
RETURNS VARCHAR AS '
DECLARE
    v_name ALIAS FOR $1;
    v_version ALIAS FOR $2;
BEGIN
    IF v_version IS NULL THEN
        return v_name;
    END IF;
    RETURN v_name || ''/'' || v_version;
END;
' LANGUAGE 'plpgsql';

Example 19-6. ¾�δؿ�����Ф��ؿ�

�ʲ��μ�³���� SELECT ʸ����ιԤ�Ȥäơ���Ψ�Τ���� IF ʸ�Ƿ�̤����ʴؿ��ˤ��ᤳ��Ǥ��ޤ���PostgreSQL �Ȥϥ������롢FOR�롼�ס�ñ�������򥨥������פ���ɬ�פ�����Ȥ����Ȥ����㤤�˵��Ť��Ǥ��礦��

CREATE OR REPLACE PROCEDURE cs_update_referrer_type_proc IS
    CURSOR referrer_keys IS    
        SELECT * FROM cs_referrer_keys 
        ORDER BY try_order;

    a_output VARCHAR(4000); 
BEGIN 
    a_output := 'CREATE OR REPLACE FUNCTION cs_find_referrer_type(v_host IN VARCHAR, v_domain IN VARCHAR, 
v_url IN VARCHAR) RETURN VARCHAR IS BEGIN'; 

    FOR referrer_key IN referrer_keys LOOP 
        a_output := a_output || ' IF v_' || referrer_key.kind || ' LIKE ''' || 
referrer_key.key_string || ''' THEN RETURN ''' || referrer_key.referrer_type || 
'''; END IF;'; 
    END LOOP; 

    a_output := a_output || ' RETURN NULL; END;'; 
    EXECUTE IMMEDIATE a_output; 
END; 
/ 
show errors

���ε�ǽ�� PostgreSQL �ǵ��Ҥ���Ȥ����ʤ�Ǥ��礦��

CREATE FUNCTION cs_update_referrer_type_proc() RETURNS INTEGER AS '
DECLARE
    referrer_keys RECORD;  -- FOR �ǻ��Ѥ�������ѵ����������
    a_output varchar(4000);
BEGIN
    a_output := ''CREATE FUNCTION cs_find_referrer_type(VARCHAR,VARCHAR,VARCHAR) 
                  RETURNS VARCHAR AS '''' 
                     DECLARE 
                         v_host ALIAS FOR $1; 
                         v_domain ALIAS FOR $2; 
                         v_url ALIAS FOR $3;
                     BEGIN ''; 

    -- 
    -- FOR <record> ��ʸ����Ѥ��� FOR �롼�פǡ��䤤��碌�η��
    -- ��ɤΤ褦�˥�����󤷤Ƥ��뤫�����դ��Ʋ�������
    --

    FOR referrer_keys IN SELECT * FROM cs_referrer_keys ORDER BY try_order LOOP
        a_output := a_output || '' IF v_'' || referrer_keys.kind || '' LIKE '''''''''' 
                 || referrer_keys.key_string || '''''''''' THEN RETURN '''''' 
                 || referrer_keys.referrer_type || ''''''; END IF;''; 
    END LOOP; 
  
    a_output := a_output || '' RETURN NULL; END; '''' LANGUAGE ''''plpgsql'''';''; 
    -- ����Ϥɤ��ѿ����֤������Ƥ��ʤ��ΤǤ��ޤ������ޤ���
    -- ����ʤ���м��Ԥ��ޤ����ؿ���¹Ԥ���¾����ˡ�ˤĤ��Ƥ� PERFORM �򻲾Ȥ��Ʋ�������
    
    EXECUTE a_output;
END;
' LANGUAGE 'plpgsql';

Example 19-7. ���������ʸ�������ץ���������ȡ�OUT �ѥ�᡼��

�ʲ��Υ��饯��� PL/SQL �ץ���������ϡ�URL ��ѡ������Ƥ����Ĥ�������(�ۥ��ȡ��ѥ����䤤��碌)���֤��ޤ���PL/pgSQL �ؿ��ϰ�Ĥ��ͤΤߤ��֤��Τǰ�ĤΥץ���������Ȥʤ�ޤ�(Section 19.11.3�򻲾Ȥ��Ƥ�������)��PostgreSQL �ǤϤ��Υץ����������3�Ĥΰۤʤ�ؿ���ʬ�䤹����ˡ������ޤ�����Ĥϥۥ��Ȥ��֤����⤦��Ĥϥѥ����֤����⤦��Ĥ��䤤��碌���֤��ޤ���

CREATE OR REPLACE PROCEDURE cs_parse_url(
    v_url IN VARCHAR,
    v_host OUT VARCHAR,  -- ����ϸƤӽФ������Ϥ���ޤ���
    v_path OUT VARCHAR,  -- �����Ʊ�͡�
    v_query OUT VARCHAR) -- �����Ǥ���
is
    a_pos1 INTEGER;
    a_pos2 INTEGER;
begin
    v_host := NULL;
    v_path := NULL;
    v_query := NULL;
    a_pos1 := instr(v_url, '//'); -- PostgreSQL �ˤ� instr �ؿ��Ϥ���ޤ���

    IF a_pos1 = 0 THEN
        RETURN;
    END IF;    
    a_pos2 := instr(v_url, '/', a_pos1 + 2);
    IF a_pos2 = 0 THEN
        v_host := substr(v_url, a_pos1 + 2);
        v_path := '/';
        RETURN;
    END IF;

    v_host := substr(v_url, a_pos1 + 2, a_pos2 - a_pos1 - 2);
    a_pos1 := instr(v_url, '?', a_pos2 + 1);

    IF a_pos1 = 0 THEN
        v_path := substr(v_url, a_pos2);
        RETURN;
    END IF;

    v_path := substr(v_url, a_pos2, a_pos1 - a_pos2);
    v_query := substr(v_url, a_pos1 + 1);
END;
/
show errors;

PostgreSQL �˰ܿ������Τ����Τ��μ�³���Ǥ���

CREATE OR REPLACE FUNCTION cs_parse_url_host(VARCHAR) RETURNS VARCHAR AS '
DECLARE
    v_url ALIAS FOR $1;
    v_host VARCHAR;
    v_path VARCHAR;
    a_pos1 INTEGER;
    a_pos2 INTEGER;
    a_pos3 INTEGER;
BEGIN
    v_host := NULL;
    a_pos1 := instr(v_url,''//'');

    IF a_pos1 = 0 THEN
        RETURN '''';  -- ��ʸ�����֤��ޤ���
    END IF;

    a_pos2 := instr(v_url,''/'',a_pos1 + 2); 
    IF a_pos2 = 0 THEN 
        v_host := substr(v_url, a_pos1 + 2); 
        v_path := ''/''; 
        RETURN v_host; 
    END IF; 

    v_host := substr(v_url, a_pos1 + 2, a_pos2 - a_pos1 - 2 ); 
    RETURN v_host; 
END; 
' LANGUAGE 'plpgsql';

Note: PostgreSQL �ˤ�instr �ؿ��Ϥ���ޤ��󤬡�����¾�δؿ����Ȥ߹�碌�ƻ��Ѥ��뤳�ȤǤ���ư��򤵤��뤳�ȤǤ��ޤ�����Ϥ���ˤ����Ƥ��ޤ��� Oracle�δؿ��˻����ȼ��� instr �ؿ���������ޤ���(����Ǻ��夬���ڤˤʤ�ޤ�)�������ɤ�Section 19.11.6 �򻲾Ȥ��Ʋ�������

19.11.3. �ץ���������

����Ū���֤��ͤ���ɬ�פ�����ޤ���Τǡ����饯��Υץ���������ϳ�ȯ�Ԥˤ���������Ϳ���Ƥ��ޤ��� INOUT �� OUT �ѥ�᡼����Ȥ����Ȥ��Ǥ��ޤ���

�ʲ�����򼨤��ޤ���

CREATE OR REPLACE PROCEDURE cs_create_job(v_job_id IN INTEGER) IS
    a_running_job_count INTEGER;
    PRAGMA AUTONOMOUS_TRANSACTION;(1)
BEGIN
    LOCK TABLE cs_jobs IN EXCLUSIVE MODE;(2)

    SELECT count(*) INTO a_running_job_count 
    FROM cs_jobs
    WHERE end_stamp IS NULL;

    IF a_running_job_count > 0 THEN
        COMMIT; -- ���å���������ޤ���(3)
        raise_application_error(-20000, 'Unable to create a new job: a job is currently running.');
    END IF;

    DELETE FROM cs_active_job;
    INSERT INTO cs_active_job(job_id) VALUES (v_job_id);

    BEGIN
        INSERT INTO cs_jobs (job_id, start_stamp) VALUES (v_job_id, sysdate);
        EXCEPTION WHEN dup_val_on_index THEN NULL; -- ���Ǥ�¸�ߤ��Ƥ��Ƥ����ꤢ��ޤ���(4)
    END;
    COMMIT;
END;
/
show errors

���Τ褦�ʼ�³����INTEGER�����֤� PostgreSQL �δؿ��˴�ñ���Ѵ����뤳�Ȥ��Ǥ��ޤ�������ϰʲ��Τ褦�ʻ��򶵤��Ƥ���뤳�Ȥ⤢�äƤ��μ�³�����äˤ��⤷�����Ǥ���

(1)
PostgreSQL �ˤ� pragmaʸ�Ϥ���ޤ���
(2)
PL/pgSQL �� LOCK TABLE����Ѥ���ʤ�С��ȥ�󥶥������ν�λ��ƤӽФ��ޤǥ��å�����������ޤ���
(3)
PL/pgSQL �ץ���������Ǥϥȥ�󥶥���������Ѥ��뤳�Ȥ��Ǥ��ޤ��󡣴ؿ�����(�Ȥ�������ƤФ��¾�δؿ�)�ϰ�ĤΥȥ�󥶥������Ǽ¹Ԥ���ޤ��� �����ơ��������꤬�������顢 PostgreSQL �Ϥ��η�̤������Хå����ޤ������Τ��ᡢBEGIN ʸ�ϰ��٤����¹ԤǤ��ޤ���
(4)
IF ʸ���֤�������ɬ�פΤ����㳰��

����Ǥϡ����Υץ���������� PL/pgSQL �˰ܿ����뤳�Ȥ��Ǥ�����ˡ��1�Ĥ򸫤Ƥߤޤ��礦��

CREATE OR REPLACE FUNCTION cs_create_job(INTEGER) RETURNS INTEGER AS '
DECLARE
    v_job_id ALIAS FOR $1;
    a_running_job_count INTEGER;
    a_num INTEGER;
    -- PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
    LOCK TABLE cs_jobs IN EXCLUSIVE MODE;
    SELECT count(*) INTO a_running_job_count 
    FROM cs_jobs 
    WHERE end_stamp IS NULL;

    IF a_running_job_count > 0
    THEN
        -- COMMIT; -- ���å���������ޤ���
        RAISE EXCEPTION ''Unable to create a new job: a job is currently running.'';
    END IF;

    DELETE FROM cs_active_job;
    INSERT INTO cs_active_job(job_id) VALUES (v_job_id);

    SELECT count(*) into a_num 
    FROM cs_jobs 
    WHERE job_id=v_job_id;
    IF NOT FOUND THEN  -- �Ǹ���䤤��碌�Dz����֤�ʤ��ä����
        -- ���Υ���֤ϥơ��֥���ˤ���ޤ���Τǡ�������������ޤ���
        INSERT INTO cs_jobs(job_id, start_stamp) VALUES (v_job_id, sysdate());
        RETURN 1;
    ELSE
        RAISE NOTICE ''Job already running.'';(1)
    END IF;

    RETURN 0;
END;
' LANGUAGE 'plpgsql';

(1)
PL/pgSQL �� notice(�⤷���� error )��ɤΤ褦�˵��������Ȥ������դ��Ʋ�������

19.11.4. �ѥå�����

Note: ��Ϥ��ޤ�ѥå�������Ȥä����Ȥ��ʤ��Τǡ��ְ㤤������ޤ����顢����Τ餻�Ƥ���������

�ѥå������ϥ��饯�뤬��Java���饹�Τ褦�ˡ�1�Ĥμ��Τ� PL/SQL ʸ�ȴؿ��򥫥ץ��벽���뤿�����ˡ�Ǥ��� �����Ǥϥ᥽�åɤȥ��֥������Ȥ�����򤷤ޤ��� �����ϥ��֥�������/�᥽�åɤ�"."(�ɥå�)��Ȥäƥ����������뤳�Ȥ��Ǥ��ޤ��������� ACS 4 �Υ��饯��Υѥå��������㤬����ޤ��� ArsDigita Community System)

CREATE OR REPLACE PACKAGE BODY acs
AS
  FUNCTION add_user (
    user_id     IN users.user_id%TYPE DEFAULT NULL,
    object_type     IN acs_objects.object_type%TYPE DEFAULT 'user',
    creation_date   IN acs_objects.creation_date%TYPE DEFAULT sysdate,
    creation_user   IN acs_objects.creation_user%TYPE DEFAULT NULL,
    creation_ip     IN acs_objects.creation_ip%TYPE DEFAULT NULL,
  ...
  ) RETURN users.user_id%TYPE
  IS
    v_user_id       users.user_id%TYPE;
    v_rel_id        membership_rels.rel_id%TYPE;
  BEGIN
    v_user_id := acs_user.new (user_id, object_type, creation_date,
                creation_user, creation_ip, email, ...
    RETURN v_user_id;
  END;
END acs;
/
show errors

ɸ��Ū��̿�ᵬ§����Ĵؿ��Ȥ��ƥ��饯�롦�ѥå������ΰۤʤ륪�֥������Ȥ�������뤳�ȤǤ���� PostgreSQL �˰ܿ����ޤ��� PostgreSQL �ؿ��ˤϥǥե���ȥѥ�᡼������˳���Ƥ���ʤɤΤ����Ĥ��κ٤������Ȥ����դ�Ϥ��ʤ���Ф����ޤ��󡢾嵭�Υѥå������Ϥ��Τ褦�ʤ�Τˤʤ�Ǥ��礦��

CREATE FUNCTION acs__add_user(INTEGER,INTEGER,VARCHAR,TIMESTAMP,INTEGER,INTEGER,...)
RETURNS INTEGER AS '
DECLARE
    user_id ALIAS FOR $1;
    object_type ALIAS FOR $2;
    creation_date ALIAS FOR $3;
    creation_user ALIAS FOR $4;
    creation_ip ALIAS FOR $5;
    ...
    v_user_id users.user_id%TYPE;
    v_rel_id membership_rels.rel_id%TYPE;
BEGIN
    v_user_id := acs_user__new(user_id,object_type,creation_date,creation_user,creation_ip, ...);
    ...

    RETURN v_user_id;
END;
' LANGUAGE 'plpgsql';

19.11.5. ���դ��٤�����¾�λ���

19.11.5.1. EXECUTE

PostgreSQL �� EXECUTE���ɤ��Ǥ��Ƥ��ޤ��� ������Section 19.5.4����������Ƥ��� quote_literal(TEXT) �� quote_string(TEXT) ��Ȥ����Ȥ�Ф��Ƥ����ʤ���Ф����ޤ��󡣤����δؿ�����Ѥ��ʤ��¤�EXECUTE ''SELECT * from $1'';�Ȥ�����ʸ��ư��ޤ���

19.11.5.2. PL/pgSQL�ؿ��κ�Ŭ��

PostgreSQL �ϼ¹Ԥ��Ŭ�����뤿���2�Ĥδؿ����������Ҥ�����ޤ��� iscachable (Ʊ��������Ϳ����줿�����Ʊ����̤��֤��ޤ�) �� isstrict (�����Τ����줫�� NULL ���ޤޤ���� NULL ���֤��ޤ�). �ܺ٤� CREATE FUNCTION �򻲾Ȥ��Ʋ�������

�����κ�Ŭ��°�������Ѥ��뤿��ˤϡ�CREATE FUNCTION ʸ��WITH �����Ҥ���Ѥ��ʤ���Ф����ޤ��󡣰ʲ�����򼨤��ޤ���

CREATE FUNCTION foo(...) RETURNS INTEGER AS '
...
' LANGUAGE 'plpgsql'
WITH (isstrict, iscachable);

19.11.6. ��Ͽ

19.11.6.1. instr �ؿ��Υ�����

--
-- Oracle �Τ�Τ�Ʊ��ư��򤹤� instr �ؿ�
-- ��ʸ :instr(string1,string2,[n],[m]) ��������[]�Ͼ�ά��ǽ�ʥѥ�᡼��
-- 
-- string1 ��� n���ܤ�ʸ������m���ܤ�ʸ���ޤǤ� string2 ��õ���ޤ���
-- n ����ξ�硢�������˸������ޤ��� m���Ϥ���ʤ��ä����ϡ�1 �� 
-- �ߤʤ��ޤ�(�ǽ��ʸ�����鸡����Ϥ�ޤ�)��
--
-- ��� : Roberto Mello ([email protected])
-- ���� : Robert Gaszewski ([email protected])
-- GPL v2�ʹߤˤ��饤����
--

CREATE FUNCTION instr(VARCHAR,VARCHAR) RETURNS INTEGER AS '
DECLARE
    pos integer;
BEGIN
    pos:= instr($1,$2,1);
    RETURN pos;
END;
' LANGUAGE 'plpgsql';


CREATE FUNCTION instr(VARCHAR,VARCHAR,INTEGER) RETURNS INTEGER AS '
DECLARE
    string ALIAS FOR $1;
    string_to_search ALIAS FOR $2;
    beg_index ALIAS FOR $3;
    pos integer NOT NULL DEFAULT 0;
    temp_str VARCHAR;
    beg INTEGER;
    length INTEGER;
    ss_length INTEGER;
BEGIN
    IF beg_index > 0 THEN

       temp_str := substring(string FROM beg_index);
       pos := position(string_to_search IN temp_str);

       IF pos = 0 THEN
	         RETURN 0;
	     ELSE
	         RETURN pos + beg_index - 1;
	     END IF;
    ELSE
       ss_length := char_length(string_to_search);
       length := char_length(string);
       beg := length + beg_index - ss_length + 2;

       WHILE beg > 0 LOOP
           temp_str := substring(string FROM beg FOR ss_length);
	         pos := position(string_to_search IN temp_str);

	         IF pos > 0 THEN
		           RETURN beg;
	         END IF;

	         beg := beg - 1;
       END LOOP;
       RETURN 0;
    END IF;
END;
' LANGUAGE 'plpgsql';

--
-- ��� : Robert Gaszewski ([email protected])
-- GPL v2�ʹߤˤ��饤����
--
CREATE FUNCTION instr(VARCHAR,VARCHAR,INTEGER,INTEGER) RETURNS INTEGER AS '
DECLARE
    string ALIAS FOR $1;
    string_to_search ALIAS FOR $2;
    beg_index ALIAS FOR $3;
    occur_index ALIAS FOR $4;
    pos integer NOT NULL DEFAULT 0;
    occur_number INTEGER NOT NULL DEFAULT 0;
    temp_str VARCHAR;
    beg INTEGER;
    i INTEGER;
    length INTEGER;
    ss_length INTEGER;
BEGIN
    IF beg_index > 0 THEN
        beg := beg_index;
        temp_str := substring(string FROM beg_index);

        FOR i IN 1..occur_index LOOP
            pos := position(string_to_search IN temp_str);

            IF i = 1 THEN
                beg := beg + pos - 1;
            ELSE
                beg := beg + pos;
            END IF;

            temp_str := substring(string FROM beg + 1);
        END LOOP;

        IF pos = 0 THEN
            RETURN 0;
        ELSE
            RETURN beg;
        END IF;
    ELSE
        ss_length := char_length(string_to_search);
        length := char_length(string);
        beg := length + beg_index - ss_length + 2;

        WHILE beg > 0 LOOP
            temp_str := substring(string FROM beg FOR ss_length);
            pos := position(string_to_search IN temp_str);

            IF pos > 0 THEN
                occur_number := occur_number + 1;

                IF occur_number = occur_index THEN
                    RETURN beg;
                END IF;
            END IF;

            beg := beg - 1;
        END LOOP;

        RETURN 0;
    END IF;
END;
' LANGUAGE 'plpgsql';