Movatterモバイル変換


[0]ホーム

URL:



Facebook
Postgres Pro
Facebook
Downloads
41.13. Porting fromOracle PL/SQL
Prev UpChapter 41. PL/pgSQLSQL Procedural LanguageHome Next

41.13. Porting fromOracle PL/SQL#

This section explains differences betweenPostgres Pro'sPL/pgSQL language and Oracle'sPL/SQL language, to help developers who port applications fromOracle® toPostgres Pro.

PL/pgSQL is similar to PL/SQL in many aspects. It is a block-structured, imperative language, and all variables have to be declared. Assignments, loops, and conditionals are similar. The main differences you should keep in mind when porting fromPL/SQL toPL/pgSQL are:

  • If a name used in an SQL command could be either a column name of a table used in the command or a reference to a variable of the function,PL/SQL treats it as a column name. By default,PL/pgSQL will throw an error complaining that the name is ambiguous. You can specifyplpgsql.variable_conflict =use_column to change this behavior to matchPL/SQL, as explained inSection 41.11.1. It's often best to avoid such ambiguities in the first place, but if you have to port a large amount of code that depends on this behavior, settingvariable_conflict may be the best solution.

  • InPostgres Pro the function body must be written as a string literal. Therefore you need to use dollar quoting or escape single quotes in the function body. (SeeSection 41.12.1.)

  • Data type names often need translation. For example, in Oracle string values are commonly declared as being of typevarchar2, which is a non-SQL-standard type. InPostgres Pro, use typevarchar ortext instead. Similarly, replace typenumber withnumeric, or use some other numeric data type if there's a more appropriate one.

  • Instead of packages, use schemas to organize your functions into groups.

  • Since there are no packages, there are no package-level variables either. This is somewhat annoying. You can keep per-session state in temporary tables instead.

  • IntegerFOR loops withREVERSE work differently:PL/SQL counts down from the second number to the first, whilePL/pgSQL counts down from the first number to the second, requiring the loop bounds to be swapped when porting. This incompatibility is unfortunate but is unlikely to be changed. (SeeSection 41.6.5.5.)

  • FOR loops over queries (other than cursors) also work differently: the target variable(s) must have been declared, whereasPL/SQL always declares them implicitly. An advantage of this is that the variable values are still accessible after the loop exits.

  • There are various notational differences for the use of cursor variables.

41.13.1. Porting Examples#

Example 41.9 shows how to port a simple function fromPL/SQL toPL/pgSQL.

Example 41.9. Porting a Simple Function fromPL/SQL toPL/pgSQL

Here is anOraclePL/SQL function:

CREATE OR REPLACE FUNCTION cs_fmt_browser_version(v_name varchar2,                                                  v_version varchar2)RETURN varchar2 ISBEGIN    IF v_version IS NULL THEN        RETURN v_name;    END IF;    RETURN v_name || '/' || v_version;END;/show errors;

Let's go through this function and see the differences compared toPL/pgSQL:

  • The type namevarchar2 has to be changed tovarchar ortext. In the examples in this section, we'll usevarchar, buttext is often a better choice if you do not need specific string length limits.

  • TheRETURN key word in the function prototype (not the function body) becomesRETURNS inPostgres Pro. Also,IS becomesAS, and you need to add aLANGUAGE clause becausePL/pgSQL is not the only possible function language.

  • InPostgres Pro, the function body is considered to be a string literal, so you need to use quote marks or dollar quotes around it. This substitutes for the terminating/ in the Oracle approach.

  • Theshow errors command does not exist inPostgres Pro, and is not needed since errors are reported automatically.

This is how this function would look when ported toPostgres Pro:

CREATE OR REPLACE FUNCTION cs_fmt_browser_version(v_name varchar,                                                  v_version varchar)RETURNS varchar AS $$BEGIN    IF v_version IS NULL THEN        RETURN v_name;    END IF;    RETURN v_name || '/' || v_version;END;$$ LANGUAGE plpgsql;


Example 41.10 shows how to port a function that creates another function and how to handle the ensuing quoting problems.

Example 41.10. Porting a Function that Creates Another Function fromPL/SQL toPL/pgSQL

The following procedure grabs rows from aSELECT statement and builds a large function with the results inIF statements, for the sake of efficiency.

This is the Oracle version:

CREATE OR REPLACE PROCEDURE cs_update_referrer_type_proc IS    CURSOR referrer_keys IS        SELECT * FROM cs_referrer_keys        ORDER BY try_order;    func_cmd VARCHAR(4000);BEGIN    func_cmd := 'CREATE OR REPLACE FUNCTION cs_find_referrer_type(v_host IN VARCHAR2,                 v_domain IN VARCHAR2, v_url IN VARCHAR2) RETURN VARCHAR2 IS BEGIN';    FOR referrer_key IN referrer_keys LOOP        func_cmd := func_cmd ||          ' IF v_' || referrer_key.kind          || ' LIKE ''' || referrer_key.key_string          || ''' THEN RETURN ''' || referrer_key.referrer_type          || '''; END IF;';    END LOOP;    func_cmd := func_cmd || ' RETURN NULL; END;';    EXECUTE IMMEDIATE func_cmd;END;/show errors;

Here is how this function would end up inPostgres Pro:

CREATE OR REPLACE PROCEDURE cs_update_referrer_type_proc() AS $func$DECLARE    referrer_keys CURSOR IS        SELECT * FROM cs_referrer_keys        ORDER BY try_order;    func_body text;    func_cmd text;BEGIN    func_body := 'BEGIN';    FOR referrer_key IN referrer_keys LOOP        func_body := func_body ||          ' IF v_' || referrer_key.kind          || ' LIKE ' || quote_literal(referrer_key.key_string)          || ' THEN RETURN ' || quote_literal(referrer_key.referrer_type)          || '; END IF;' ;    END LOOP;    func_body := func_body || ' RETURN NULL; END;';    func_cmd :=      'CREATE OR REPLACE FUNCTION cs_find_referrer_type(v_host varchar,                                                        v_domain varchar,                                                        v_url varchar)        RETURNS varchar AS '      || quote_literal(func_body)      || ' LANGUAGE plpgsql;' ;    EXECUTE func_cmd;END;$func$ LANGUAGE plpgsql;

Notice how the body of the function is built separately and passed throughquote_literal to double any quote marks in it. This technique is needed because we cannot safely use dollar quoting for defining the new function: we do not know for sure what strings will be interpolated from thereferrer_key.key_string field. (We are assuming here thatreferrer_key.kind can be trusted to always behost,domain, orurl, butreferrer_key.key_string might be anything, in particular it might contain dollar signs.) This function is actually an improvement on the Oracle original, because it will not generate broken code whenreferrer_key.key_string orreferrer_key.referrer_type contain quote marks.


Example 41.11 shows how to port a function withOUT parameters and string manipulation.Postgres Pro does not have a built-ininstr function, but you can create one using a combination of other functions. InSection 41.13.3 there is aPL/pgSQL implementation ofinstr that you can use to make your porting easier.

Example 41.11. Porting a Procedure With String Manipulation andOUT Parameters fromPL/SQL toPL/pgSQL

The followingOracle PL/SQL procedure is used to parse a URL and return several elements (host, path, and query).

This is the Oracle version:

CREATE OR REPLACE PROCEDURE cs_parse_url(    v_url IN VARCHAR2,    v_host OUT VARCHAR2,  -- This will be passed back    v_path OUT VARCHAR2,  -- This one too    v_query OUT VARCHAR2) -- And this oneIS    a_pos1 INTEGER;    a_pos2 INTEGER;BEGIN    v_host := NULL;    v_path := NULL;    v_query := 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;    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;

Here is a possible translation intoPL/pgSQL:

CREATE OR REPLACE FUNCTION cs_parse_url(    v_url IN VARCHAR,    v_host OUT VARCHAR,  -- This will be passed back    v_path OUT VARCHAR,  -- This one too    v_query OUT VARCHAR) -- And this oneAS $$DECLARE    a_pos1 INTEGER;    a_pos2 INTEGER;BEGIN    v_host := NULL;    v_path := NULL;    v_query := 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;    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;$$ LANGUAGE plpgsql;

This function could be used like this:

SELECT * FROM cs_parse_url('http://foobar.com/query.cgi?baz');


Example 41.12 shows how to port a procedure that uses numerous features that are specific to Oracle.

Example 41.12. Porting a Procedure fromPL/SQL toPL/pgSQL

The Oracle version:

CREATE OR REPLACE PROCEDURE cs_create_job(v_job_id IN INTEGER) IS    a_running_job_count INTEGER;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; -- free lock        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, now());    EXCEPTION        WHEN dup_val_on_index THEN NULL; -- don't worry if it already exists    END;    COMMIT;END;/show errors

This is how we could port this procedure toPL/pgSQL:

CREATE OR REPLACE PROCEDURE cs_create_job(v_job_id integer) AS $$DECLARE    a_running_job_count integer;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; -- free lock        RAISE EXCEPTION 'Unable to create a new job: a job is currently running'; --(1)    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, now());    EXCEPTION        WHEN unique_violation THEN --(2)            -- don't worry if it already exists    END;    COMMIT;END;$$ LANGUAGE plpgsql;

(1)

The syntax ofRAISE is considerably different from Oracle's statement, although the basic caseRAISEexception_name works similarly.

(2)

The exception names supported byPL/pgSQL are different from Oracle's. The set of built-in exception names is much larger (seeAppendix A). There is not currently a way to declare user-defined exception names, although you can throw user-chosen SQLSTATE values instead.


41.13.2. Other Things to Watch For#

This section explains a few other things to watch for when porting OraclePL/SQL functions toPostgres Pro.

41.13.2.1. Implicit Rollback after Exceptions#

InPL/pgSQL, when an exception is caught by anEXCEPTION clause, all database changes since the block'sBEGIN are automatically rolled back. That is, the behavior is equivalent to what you'd get in Oracle with:

BEGIN    SAVEPOINT s1;    ... code here ...EXCEPTION    WHEN ... THEN        ROLLBACK TO s1;        ... code here ...    WHEN ... THEN        ROLLBACK TO s1;        ... code here ...END;

If you are translating an Oracle procedure that usesSAVEPOINT andROLLBACK TO in this style, your task is easy: just omit theSAVEPOINT andROLLBACK TO. If you have a procedure that usesSAVEPOINT andROLLBACK TO in a different way then some actual thought will be required.

41.13.2.2. EXECUTE#

ThePL/pgSQL version ofEXECUTE works similarly to thePL/SQL version, but you have to remember to usequote_literal andquote_ident as described inSection 41.5.4. Constructs of the typeEXECUTE 'SELECT * FROM $1'; will not work reliably unless you use these functions.

41.13.2.3. OptimizingPL/pgSQL Functions#

Postgres Pro gives you two function creation modifiers to optimize execution:volatility (whether the function always returns the same result when given the same arguments) andstrictness (whether the function returns null if any argument is null). Consult theCREATE FUNCTION reference page for details.

When making use of these optimization attributes, yourCREATE FUNCTION statement might look something like this:

CREATE FUNCTION foo(...) RETURNS integer AS $$...$$ LANGUAGE plpgsql STRICT IMMUTABLE;

41.13.3. Appendix#

This section contains the code for a set of Oracle-compatibleinstr functions that you can use to simplify your porting efforts.

---- instr functions that mimic Oracle's counterpart-- Syntax: instr(string1, string2 [, n [, m]])-- where [] denotes optional parameters.---- Search string1, beginning at the nth character, for the mth occurrence-- of string2.  If n is negative, search backwards, starting at the abs(n)'th-- character from the end of string1.-- If n is not passed, assume 1 (search starts at first character).-- If m is not passed, assume 1 (find first occurrence).-- Returns starting index of string2 in string1, or 0 if string2 is not found.--CREATE FUNCTION instr(varchar, varchar) RETURNS integer AS $$BEGIN    RETURN instr($1, $2, 1);END;$$ LANGUAGE plpgsql STRICT IMMUTABLE;CREATE FUNCTION instr(string varchar, string_to_search_for varchar,                      beg_index integer)RETURNS integer AS $$DECLARE    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_for IN temp_str);        IF pos = 0 THEN            RETURN 0;        ELSE            RETURN pos + beg_index - 1;        END IF;    ELSIF beg_index < 0 THEN        ss_length := char_length(string_to_search_for);        length := char_length(string);        beg := length + 1 + beg_index;        WHILE beg > 0 LOOP            temp_str := substring(string FROM beg FOR ss_length);            IF string_to_search_for = temp_str THEN                RETURN beg;            END IF;            beg := beg - 1;        END LOOP;        RETURN 0;    ELSE        RETURN 0;    END IF;END;$$ LANGUAGE plpgsql STRICT IMMUTABLE;CREATE FUNCTION instr(string varchar, string_to_search_for varchar,                      beg_index integer, occur_index integer)RETURNS integer AS $$DECLARE    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 occur_index <= 0 THEN        RAISE 'argument ''%'' is out of range', occur_index          USING ERRCODE = '22003';    END IF;    IF beg_index > 0 THEN        beg := beg_index - 1;        FOR i IN 1..occur_index LOOP            temp_str := substring(string FROM beg + 1);            pos := position(string_to_search_for IN temp_str);            IF pos = 0 THEN                RETURN 0;            END IF;            beg := beg + pos;        END LOOP;        RETURN beg;    ELSIF beg_index < 0 THEN        ss_length := char_length(string_to_search_for);        length := char_length(string);        beg := length + 1 + beg_index;        WHILE beg > 0 LOOP            temp_str := substring(string FROM beg FOR ss_length);            IF string_to_search_for = temp_str 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;    ELSE        RETURN 0;    END IF;END;$$ LANGUAGE plpgsql STRICT IMMUTABLE;

Prev Up Next
41.12. Tips for Developing inPL/pgSQL Home Chapter 42. PL/Tcl — Tcl Procedural Language
pdfepub
Go to Postgres Pro Standard 17
By continuing to browse this website, you agree to the use of cookies. Go toPrivacy Policy.

[8]ページ先頭

©2009-2025 Movatter.jp