×
Мы обрабатываем cookies, чтобы сделать наш сайт удобнее и персонализированнее для вас. Подробнее: политика использования «cookies» и «политики конфиденциальности».

Для самостоятельной настройки ознакомьтесь с инструкцией

Дополнительные настройки cookies в браузерах

Файлы cookie автоматически загружаются в ваш браузер при посещении веб-сайта. У вас есть возможность управлять этими файлами. Если Вы не согласны с использованием файлов cookies, запретите их сохранение на своём устройстве, удалите уже имеющиеся файлы cookies через настройки браузера или прекратите использование сайта.

При отключении обработки cookie наш сайт продолжит функционировать, однако будут использоваться исключительно необходимые технические файлы, без которых работа ресурса невозможна.

Инструкция по отключению cookies
Принять
Настроить
Отклонить

ДОКУМЕНТАЦИЯ

Выберите версию, форк и язык для СУБД Digital Q.DataBase, чтобы прочитать или скачать всю документацию.
Техподдержка
Документация
Диасофт
Авторские права © 2016–2025 ООО "Диасофт Экосистема"
Скачать всю документацию:

5.6.13. Портирование из Oracle PL/SQL

5.6.13.1. Примеры переноса
5.6.13.2. Дополнительные особенности
5.6.13.3. Приложение

В данном разделе описываются различия между Digital Q.DataBase PL/pgSQL языком и языком Oracle PL/SQL языком, в помощь разработчикам, переносящим приложения из Oracle® на Digital Q.DataBase.

PL/pgSQL во многом похож на PL/SQL. Он является блочно-структурированным императивным языком, в котором необходимо объявлять все переменные. Операции присваивания, циклы и условные конструкции аналогичны. Основные различия, которые следует учитывать при переносе из PL/SQL для того чтобы PL/pgSQL следующие:

  • Если имя, используемое в команде SQL, может быть как именем столбца таблицы, задействованной в команде, так и ссылкой на переменную функции, PL/SQL оно интерпретируется как имя столбца. По умолчанию PL/pgSQL будет выдана ошибка с сообщением о неоднозначности имени. Можно указать параметр plpgsql.variable_conflict = use_column для изменения этого поведения в соответствии с PL/SQL, как описано в Раздел 5.6.11.1. Прежде всего рекомендуется избегать подобных неоднозначностей, но если требуется перенести большой объем кода, зависящий от такого поведения, установка значения variable_conflict может оказаться оптимальным решением.

  • В Digital Q.DataBase тело функции должно быть представлено в виде строкового литерала. Следовательно, необходимо использовать экранирование долларами или дублировать одинарные кавычки в теле функции (см. Раздел 5.6.12.1.)

  • Названия типов данных часто требуют перевода. Например, в Oracle строковые значения обычно объявляются с типом данных varchar2, которые не является стандартным типом SQL. В Digital Q.DataBase, используйте тип данных varchar или text вместо него. Аналогичным образом замените тип данных число на numeric, либо используйте иной числовой тип данных, если имеется более подходящий.

  • Для группировки функций вместо пакетов следует использовать схемы.

  • Поскольку пакеты отсутствуют, переменные уровня пакета также не предусмотрены. Это создает определенные неудобства. Состояние сеанса можно сохранять вместо этого во временных таблицах.

  • Integer FOR циклы с REVERSE работают иначе: PL/SQL выполняет обратный отсчёт от второго числа к первому, тогда как PL/pgSQL выполняет обратный отсчёт от первого числа ко второму, что требует перестановки границ цикла при переносе. Данная несовместимость нежелательна, но её изменение маловероятно. (См. Раздел 5.6.6.5.5.)

  • FOR циклы по результатам запросов (за исключением курсоров) также работают иначе: целевые переменные должны быть предварительно объявлены, в то время как PL/SQL всегда объявляет их неявно. Преимущество этого подхода заключается в том, что значения переменных остаются доступными после выхода из цикла.

  • Существуют различные синтаксические различия при использовании курсорных переменных.

5.6.13.1. Примеры переноса #

Пример 5.6.9 показывает, как перенести простую функцию из PL/SQL на PL/pgSQL.

Пример 5.6.9. Перенос простой функции из PL/SQL на PL/pgSQL

Ниже приведен пример Oracle PL/SQL функции:

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

Рассмотрим эту функцию и проанализируем ее отличия от PL/pgSQL:

  • Имя типа varchar2 необходимо изменить на varchar или text. В примерах данного раздела будет использоваться varchar, но text часто является более предпочтительным, если не требуются конкретные ограничения длины строки.

  • RETURN ключевое слово в прототипе функции (не в теле функции) заменяется на RETURNS в Digital Q.DataBase. Кроме того, IS заменяется на AS, и требуется добавить LANGUAGE предложение, так как PL/pgSQL не является единственным возможным языком функций.

  • В Digital Q.DataBase, тело функции рассматривается как строковый литерал, поэтому необходимо использовать кавычки или знаки доллара для его обрамления. Это заменяет завершающий символ / в подходе Oracle.

  • show errors команда не существует в Digital Q.DataBase, и в ней нет необходимости, так как сообщения об ошибках выводятся автоматически.

Так будет выглядеть эта функция после переноса в Digital Q.DataBase:

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;


Пример 5.6.10 демонстрирует способ переноса функции, создающей другую функцию, и способы решения возникающих при этом проблем с использованием кавычек.

Пример 5.6.10. Портирование функции, создающей другую функцию, из PL/SQL на PL/pgSQL

Следующая процедура выбирает строки из SELECT оператора и формирует большую функцию с результатами в IF операторах в целях повышения эффективности.

Версия для Oracle:

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;

Ниже показано, как эта функция будет выглядеть в Digital Q.DataBase:

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;

Следует обратить внимание на то, что тело функции формируется отдельно и передается через quote_literal для дублирования в нем всех символов кавычек. Данный прием необходим, поскольку нельзя гарантированно использовать экранирование долларами при определении новой функции: нет уверенности в том, какие именно строки будут интерполированы из referrer_key.key_string поля. (В данном случае предполагается, что referrer_key.kind можно считать всегда host, domain, или url, но referrer_key.key_string может быть любым; в частности, оно может содержать знаки доллара.) Данная функция фактически является усовершенствованием оригинала Oracle, поскольку она не генерирует некорректный код в случаях, когда referrer_key.key_string или referrer_key.referrer_type содержит кавычки.


Пример 5.6.11 демонстрирует способ переноса функции с OUT параметрами и операциями со строками. Digital Q.DataBase не имеет встроенной instr функция, однако её можно создать путем комбинирования других функций. В Раздел 5.6.13.3 имеется PL/pgSQL реализация instr которые можно использовать для упрощения процесса переноса.

Пример 5.6.11. Перенос процедуры с использованием манипуляций со строками и OUT параметров из PL/SQL в PL/pgSQL

Следующая Oracle процедура PL/SQL используется для синтаксического анализа URL-адреса и возврата нескольких элементов (хоста, пути и запроса).

Версия для Oracle:

CREATE OR REPLACE PROCEDURE cs_parse_url(
    v_url IN VARCHAR2,
    v_host OUT VARCHAR2,  -- Данное значение будет возвращено
    v_path OUT VARCHAR2,  -- Этот параметр также будет возвращен
    v_query OUT VARCHAR2) -- И этот параметр также будет возвращен
IS
    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;

Ниже представлен возможный вариант реализации на языке PL/pgSQL:

CREATE OR REPLACE FUNCTION cs_parse_url(
    v_url IN VARCHAR,
    v_host OUT VARCHAR,  -- Данный параметр будет возвращен
    v_path OUT VARCHAR,  -- Этот параметр также будет возвращен
    v_query OUT VARCHAR) -- И этот параметр
AS $$
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;

Эту функцию можно использовать следующим образом:

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


Пример 5.6.12 демонстрирует перенос процедуры, в которой используются многочисленные специфичные для Oracle функциональные возможности.

Пример 5.6.12. Перенос процедуры из PL/SQL на PL/pgSQL

Версия Oracle:

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; -- снятие блокировки
        raise_application_error(-20000,
                 'Не удалось создать новое задание: в данный момент уже выполняется другое задание.');
    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; -- игнорировать, если запись уже существует
    END;
    COMMIT;
END;
/
show errors

Ниже показан пример переноса данной процедуры в PL/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; -- снятие блокировки
        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)
            -- игнорировать, если запись уже существует
    END;
    COMMIT;
END;
$$ LANGUAGE plpgsql;

(1)

Синтаксис RAISE существенно отличается от соответствующего оператора Oracle, хотя в базовом варианте он RAISE exception_name функционирует аналогично.

(2)

Имена исключений, поддерживаемые в PL/pgSQL выполняются; отличаются от имен в Oracle. Набор встроенных имен исключений гораздо шире (см. Приложение 8.1). В настоящее время отсутствует возможность объявлять пользовательские имена исключений, хотя вместо этого можно генерировать произвольные значения SQLSTATE.


5.6.13.2. Дополнительные особенности #

В данном разделе описываются некоторые другие аспекты, которые следует учитывать при переносе функций Oracle в PL/SQL Digital Q.DataBase.

5.6.13.2.1. Неявный откат после исключений #

В PL/pgSQL, когда исключение перехватывается в блоке EXCEPTION все изменения в базе данных, произведенные с начала этого блока BEGIN автоматически откатываются. То есть поведение эквивалентно результату, который был бы получен в Oracle при использовании:

BEGIN
    SAVEPOINT s1;
    ... код здесь ...
EXCEPTION
    WHEN ... THEN
        ROLLBACK TO s1;
        ... code here ...
    WHEN ... THEN
        ROLLBACK TO s1;
        ... code here ...
END;

При переносе процедуры Oracle, в которой используется SAVEPOINT и ROLLBACK TO в данном стиле, задача упрощается: достаточно опустить SAVEPOINT и ROLLBACK TO. При наличии процедуры, использующей SAVEPOINT и ROLLBACK TO иным образом, то потребуется тщательный анализ.

5.6.13.2.2. EXECUTE #

Предложение PL/pgSQL версия EXECUTE работает аналогично PL/SQL версии, однако следует учитывать необходимость использования quote_literal и quote_ident как описано в Раздел 5.6.5.4. Конструкции типа EXECUTE 'SELECT * FROM $1'; не будет работать надежно без использования этих функций.

5.6.13.2.3. Оптимизация PL/pgSQL Функции #

Digital Q.DataBase предоставляет два модификатора создания функций для оптимизации выполнения: «категория изменчивости» (возвращает ли функция всегда один и тот же результат при одинаковых аргументах) и «строгость» (возвращает ли функция значение null, если какой-либо из аргументов равен null). Для получения подробной информации следует обратиться к CREATE FUNCTION справочной странице.

При использовании этих атрибутов оптимизации CREATE FUNCTION оператор может выглядеть следующим образом:

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

5.6.13.3. Приложение #

В данном разделе приведен код набора Oracle-совместимых instr функций, которые можно использовать для упрощения переноса кода.

--
-- функции instr, имитирующие аналоги в Oracle
-- Синтаксис: instr(string1, string2 [, n [, m]])
-- где [] обозначает необязательные параметры.
--
-- Поиск в строке string1, начиная с n-го символа, m-го вхождения
-- строки string2.  Если параметр n имеет отрицательное значение, поиск выполняется в обратном направлении, начиная с abs(n)-го символа от конца строки string1. -- Если параметр n не указан, принимается значение 1 (поиск начинается с первого символа). -- Если параметр m не указан, принимается значение 1 (поиск первого вхождения). -- Возвращается начальный индекс строки string2 в строке string1 либо 0, если строка string2 не найдена. --  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 'аргумент ''%'' вне диапазона', 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;

Наверх
свяжитесь
с нами
контакты
Для прямой связи с нами вы можете использовать контакты ниже, либо оставить заявку через форму обратной связи, и мы обязательно свяжемся с вами

*поля обязательные к заполнению