Попробовать бесплатно

Примеры использования опций sqlways.ini секции [POSTGRE]

[Usage example]:RETURN_RESULT_FROM_SP_AND_FN

Наборы результатов в PosgreSQL могут быть возвращены следующими способами: с помощью таблицы, типа SETOF и с помощью рефкурсора. Ниже приведены примеры вывода с различными вариантами:

Исходный код (MSSQL Server)

 create table test_data(c1 int, c2 varchar(22))
 create procedure result_set_pr @p1 Date as
 select @p1 as c0, c1, c2 from test_data

Преобразованный код PostgreSQL (RETURN_RESULT_FROM_SP_AND_FN=TABLE, по умолчанию)

 CREATE OR REPLACE FUNCTION result_set_pr(v_p1 DATE)
 RETURNS table
 (
    c0 DATE,
    c1 INTEGER,
    c2 VARCHAR(22)
 ) LANGUAGE plpgsql
    AS $$
 BEGIN
    return query select v_p1 as c0, c1, c2 from test_data;
 END; $$;

Преобразованный код PostgreSQL (RETURN_RESULT_FROM_SP_AND_FN=SETOF)

 CREATE TYPE result_set_pr_rs AS(c0 DATE, c1 INTEGER, c2 VARCHAR(22));
 CREATE OR REPLACE FUNCTION result_set_pr(v_p1 DATE)
 RETURNS SETOF result_set_pr_rs LANGUAGE plpgsql
    AS $$
 BEGIN
    return query select v_p1 as c0, c1, c2 from test_data;
 END; $$;

Преобразованный код PostgreSQL (RETURN_RESULT_FROM_SP_AND_FN=REFCURSOR)

 CREATE OR REPLACE PROCEDURE result_set_pr(v_p1 DATE, INOUT SWV_RefCur refcursor)
 LANGUAGE plpgsql
    AS $$
 BEGIN
    open SWV_RefCur for
    select v_p1 as c0, c1, c2 from test_data;
 END; $$;


[Usage example]:TABLE_TYPE_CONVERSION

PostgreSQL не поддерживает коллекции, но есть 2 способа имитировать логику Oracle.

По умолчанию табличные типы (TYPE IS TABLE, VARRAYS) преобразуются с помощью массивов. Другой способ - преобразовать табличные типы в массивы в PostgreSQL (используя временные таблицы), как это показано в столбце 2 таблицы. Исходный код (Oracle)

 CREATE TYPE employee AS OBJECT (
    id NUMBER,
    Name VARCHAR(300)
 );
 CREATE TYPE employees_tab IS TABLE OF employee;

 CREATE OR REPLACE PROCEDURE hire(EMPLOYEES in out employees_tab,
 id NUMBER, Name VARCHAR) AS
 NEW_EMPLOYEES employees_tab := employees_tab();
 BEGIN
    EMPLOYEES.Extend(1);
    EMPLOYEES(EMPLOYEES.count) := employee(id, Name);
    FOR i IN EMPLOYEES.first..EMPLOYEES.last
    LOOP
       INSERT INTO emp_tab values (EMPLOYEES(i).id, EMPLOYEES(i).Name);
    END LOOP;
    INSERT INTO EMP_TAB SELECT * FROM TABLE(EMPLOYEES);
    NEW_EMPLOYEES.Extend(EMPLOYEES.count);
    NEW_EMPLOYEES.Delete;
 END;

Преобразованный код PostgreSQL (TABLE_TYPE_CONVERSION=Tables)

 CREATE TYPE EMPLOYEE AS(id DOUBLE PRECISION,Name VARCHAR(300));
 -- CREATE TYPE EMPLOYEES_TAB IS TABLE OF employee;
 CREATE OR REPLACE PROCEDURE HIRE(id DOUBLE PRECISION,Name VARCHAR(4000))
 LANGUAGE plpgsql
    AS $$
 BEGIN
    create temporary table if not exists SWT_HIRE_NEW_EMPLOYEES
    (
       SWC_INDEX INTEGER NOT NULL,
       ID DOUBLE PRECISION,
       NAME VARCHAR(300)
    );
    DELETE FROM SWT_HIRE_NEW_EMPLOYEES;
    FOR i IN COALESCE((SELECT MAX(SWT_HIRE_EMPLOYEES.SWC_INDEX) FROM SWT_HIRE_EMPLOYEES)+1,1) .. COALESCE((SELECT MAX(SWT_HIRE_EMPLOYEES.SWC_INDEX) FROM SWT_HIRE_EMPLOYEES),
    0)+1
    LOOP
       INSERT INTO SWT_HIRE_EMPLOYEES(SWC_Index) VALUES(i);
    END LOOP;
    IF NOT EXISTS(SELECT 1 FROM SWT_HIRE_EMPLOYEES WHERE SWT_HIRE_EMPLOYEES.SWC_INDEX =(SELECT COUNT(*) FROM SWT_HIRE_EMPLOYEES)) then
       INSERT INTO SWT_HIRE_EMPLOYEES   VALUES((SELECT COUNT(*) FROM SWT_HIRE_EMPLOYEES),NULL);
    END IF;
    UPDATE SWT_HIRE_EMPLOYEES SET id = id,Name = Name
    WHERE SWT_HIRE_EMPLOYEES.SWC_INDEX =(SELECT COUNT(*) FROM SWT_HIRE_EMPLOYEES);
    FOR i IN(SELECT MIN(SWC_INDEX) FROM SWT_HIRE_EMPLOYEES) ..(SELECT MAX(SWC_INDEX) FROM SWT_HIRE_EMPLOYEES)
    LOOP
       INSERT INTO EMP_TAB  values((SELECT SWT_HIRE_EMPLOYEES.ID FROM SWT_HIRE_EMPLOYEES WHERE SWC_INDEX = i), (SELECT
    SWT_HIRE_EMPLOYEES.NAME FROM SWT_HIRE_EMPLOYEES WHERE SWC_INDEX = i));
    END LOOP;
    INSERT INTO EMP_TAB  SELECT * FROM EMPLOYEES;
    FOR i IN COALESCE((SELECT MAX(SWT_HIRE_NEW_EMPLOYEES.SWC_INDEX) FROM SWT_HIRE_NEW_EMPLOYEES)+1,1) .. COALESCE((SELECT
    MAX(SWT_HIRE_NEW_EMPLOYEES.SWC_INDEX) FROM SWT_HIRE_NEW_EMPLOYEES),0)+(SELECT COUNT(*) FROM SWT_HIRE_EMPLOYEES)
    LOOP
       INSERT INTO SWT_HIRE_NEW_EMPLOYEES(SWC_Index) VALUES(i);
    END LOOP;
    DELETE FROM SWT_HIRE_NEW_EMPLOYEES;
 END; $$;

Преобразованный код PostgreSQL (TABLE_TYPE_CONVERSION=Arrays, по умолчанию)

 CREATE TYPE EMPLOYEE AS(id DOUBLE PRECISION,Name VARCHAR(300));
 -- CREATE TYPE EMPLOYEES_TAB IS TABLE OF employee;
 CREATE OR REPLACE PROCEDURE HIRE(INOUT EMPLOYEES EMPLOYEE[] ,
 ID DOUBLE PRECISION, NAME VARCHAR(4000))
 LANGUAGE plpgsql
    AS $$
    DECLARE
    EMPLOYEES_REC  EMPLOYEE;
    NEW_EMPLOYEES  EMPLOYEE[] default array[]::EMPLOYEE[] ;
 BEGIN
    EMPLOYEES[swf_array_length(EMPLOYEES)+1] := null;
    EMPLOYEES[swf_array_length(EMPLOYEES)] := row(ID,NAME);
    FOR i IN array_lower(EMPLOYEES,1) .. array_upper(EMPLOYEES,1)
    LOOP
       EMPLOYEES_REC := EMPLOYEES[i];
       INSERT INTO EMP_TAB  values(EMPLOYEES_REC.ID, EMPLOYEES_REC.NAME);
       EMPLOYEES[i] := EMPLOYEES_REC;
    END LOOP;
    INSERT INTO EMP_TAB  SELECT * FROM unnest(EMPLOYEES);
    for i in 1 .. swf_array_length(EMPLOYEES) loop
       NEW_EMPLOYEES[swf_array_length(NEW_EMPLOYEES)+1] := null;
    end loop;
    NEW_EMPLOYEES := array[]:: EMPLOYEE[];
 END; $$;


[Usage example]:IDENTITY_TO_SERIAL

PostgreSQL позволяет использовать 2 способа автоматической генерации целых чисел: с помощью свойства IDENTITY или с помощью типа псевдоданных SERIAL. Результат использования опции со значениями "No" и "Yes" показан в таблице ниже. Тип данных исходного столбца IDENTITY будет преобразован в соответствующий столбец SERIAL: SMALLINT станет SMALLSERIAL, INTEGER перейдет в SERIAL, а BIGINT в BIGSERIAL соответственно. Исходный код (DB2 LUW)

 CREATE TABLE TABIDENTCOLUMN
 (
 ID INTEGER GENERATED ALWAYS AS IDENTITY
 (START WITH 1, INCREMENT BY 1)   NOT NULL,
 NAME CHAR(5)
 );
 CREATE TABLE TABIDENTCOLUMN_2
 (
 ID SMALLINT GENERATED ALWAYS AS IDENTITY
 (START WITH 3, INCREMENT BY 1)   NOT NULL,
 NAME CHAR(5)
 )

Преобразованный код PostgreSQL (IDENTITY_TO_SERIAL=No)

 CREATE TABLE TABIDENTCOLUMN
 (
    ID  INTEGER GENERATED ALWAYS AS IDENTITY(START 1 INCREMENT 1)    NOT NULL,
    NAME  CHAR(5)
 );
 CREATE TABLE TABIDENTCOLUMN_2
 (
    ID  SMALLINT GENERATED ALWAYS AS IDENTITY(START 3 INCREMENT 1)    NOT NULL,
    NAME  CHAR(5)
 );

Преобразованный код PostgreSQL (IDENTITY_TO_SERIAL=Yes)

 CREATE TABLE TABIDENTCOLUMN
 (
    ID  SERIAL,
    NAME  CHAR(5)
 );
 CREATE TABLE TABIDENTCOLUMN_2
 (
    ID SMALLSERIAL,
    NAME  CHAR(5)
 );
 ALTER SEQUENCE TABIDENTCOLUMN_2_ID_seq RESTART WITH 3 INCREMENT BY 1;


[Usage example]:IDENTITY_COLUMN_TYPE

В PostgreSQL разрешено указывать различные типы генерации для столбца IDENTITY. Для того чтобы использовать нужный, можно установить опцию IDENTITY_COLUMN_TYPE.

Ниже показано, как опция IDENTITY_COLUMN_TYPE влияет на результат преобразования: в столбце 1 приведен пример исходного кода, в столбце 2 - преобразованный код с установленной опцией «Always» (или с пустой опцией), а в последнем столбце - преобразованный код с установленной опцией «Default». Исходный код (Microsoft SQL Server)

CREATE TABLE ident_table_pg 
(
c1 INT IDENTITY, 
c2 VARCHAR(22)
);

Преобразованный код PostgreSQL (с опцией IDENTITY_COLUMN_TYPE=ALWAYS, по умолчанию)

CREATE TABLE ident_table_pg
(
  c1 INTEGER GENERATED ALWAYS AS IDENTITY(START 1 INCREMENT 1)   NOT NULL,
  c2 VARCHAR(22) 
);

Преобразованный код PostgreSQL (с опцией IDENTITY_COLUMN_TYPE=DEFAULT)

CREATE TABLE ident_table_pg 
( 
c1 INTEGER GENERATED BY DEFAULT AS IDENTITY(START 1 INCREMENT 1) NOT NULL, 
c2 VARCHAR(22) 
);


[Usage example]:TRIGGER_RECURSION_LVL

Чтобы избежать выполнения триггеров, которые будут запускать один и тот же триггер или любые другие триггеры бесконечное количество раз, иначе говоря, рекурсии триггеров, можно добавить соответствующий оператор WHEN для проверки уровня вложенности триггеров.

В следующем примере показаны результаты преобразования триггеров в зависимости от значения опции TRIGGER_RECURSION_LVL: в столбце 1 приведен пример исходного кода триггера, в столбце 2 - преобразованная функция и код триггера, когда значение опции не установлено, а в столбце 3 - преобразованный код с установленной опцией TRIGGER_RECURSION_LVL=0. Триггер в столбце 2 приведет к его бесконечному выполнению, которое в итоге завершится ошибкой «ERROR: превышен предел глубины стека». Чтобы решить эту проблему, необходимо добавить проверку с помощью опции - это можно сделать, указав опцию, как показано в столбце 3. Исходный код (Microsoft SQL Server)

CREATE TRIGGER trigOnTab1 
ON tab1 
AFTER UPDATE 
AS 
BEGIN
  SET NOCOUNT ON
  update tab1
          set c2 = UPPER(i.c2)
          from tab1 t
          inner join inserted i on i.c1 = t.c1 
END

Преобразованный код PostgreSQL (с опцией TRIGGER_RECURSION_LVL= , по умолчанию)

CREATE OR REPLACE FUNCTION trigOnTab1_TrFunc() 
RETURNS TRIGGER LANGUAGE plpgsql
  AS $$
BEGIN
  BEGIN
  update tab1 t
  set c2 = UPPER(i.c2)
  from new_table i WHERE i.c1 = t.c1;
  END;
  RETURN NULL; 
END; $$; 

DROP TRIGGER IF EXISTS trigOnTab1 ON tab1;
CREATE TRIGGER trigOnTab1
AFTER UPDATE 
ON  tab1 
REFERENCING NEW TABLE AS new_table 
FOR STATEMENT 
EXECUTE PROCEDURE trigOnTab1_TrFunc();

Преобразованный код PostgreSQL (с опцией TRIGGER_RECURSION_LVL=0)

CREATE OR REPLACE FUNCTION trigOnTab1_TrFunc() 
RETURNS TRIGGER LANGUAGE plpgsql
  AS $$ 
BEGIN
  BEGIN
  update tab1 t
  set c2 = UPPER(i.c2)
  from new_table i WHERE i.c1 = t.c1;
  END;
  RETURN NULL; 
END; $$; 

DROP TRIGGER IF EXISTS trigOnTab1 ON tab1; 
CREATE TRIGGER trigOnTab1 
AFTER UPDATE 
ON  tab1 
REFERENCING NEW TABLE AS new_table 
FOR STATEMENT
  WHEN (pg_trigger_depth() <1)
  EXECUTE PROCEDURE trigOnTab1_TrFunc();


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

Исходный код Oracle PostgreSQL AUTONOMOUS_TRANSACTION_TO_DBLINK=No PostgreSQL AUTONOMOUS_TRANSACTION_TO_DBLINK=Yes
CREATE PROCEDURE AUTO_TEST 
(id_value number, text_value varchar2)
IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO DSSUSER.AUTONOMOUS_EVENT (id, value)
VALUES(id_value, text_value);
COMMIT;
END AUTO_TEST;
CREATE OR REPLACE PROCEDURE AUTO_TEST 
(id_value DOUBLE PRECISION, text_value VARCHAR)
LANGUAGE plpgsql
AS $$
BEGIN
INSERT INTO AUTONOMOUS_EVENT(ID, VALUE)
VALUES(id_value, text_value);
COMMIT;
END; $$;
CREATE OR REPLACE PROCEDURE AUTO_TEST 
(id_value DOUBLE PRECISION, text_value VARCHAR, IN is_recursive boolean DEFAULT false)
LANGUAGE plpgsql
AS $$
DECLARE
v_sql text;
BEGIN
IF is_recursive = FALSE THEN
begin
IF NOT EXISTS (SELECT 1 FROM dblink_get_connections()
WHERE dblink_get_connections@>'{myconn}') THEN
PERFORM dblink_connect('myconn', 'SWL_j2p9ecom_link');
END IF;
v_sql := format('CALL dssuser.AUTO_TEST( id_value => %L, text_value => %L,
is_recursive => TRUE)', id_value, text_value);
PERFORM dblink_exec( 'myconn', v_sql);
end;
ELSE
--procedure body insert into dssuser.autonomous_event values (id_value, text_value);
commit;
END IF;
END; $$;


[Usage example]:SECURITY_DEFINER

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

Исходный код Oracle По умолчанию преобразованный код PostgreSQL Преобразованный код PostgreSQL с опцией SECURITY_DEFINER=Yes
CREATE Procedure Pr_Test(p1 int default 0,p2 int, p3 int ) IS 
BEGIN
insert into tab4 (col1, col2, col3)
select p1,p2,p3 from dual;
END Pr_Test; 
CREATE OR REPLACE Procedure Pr_Test(p1 INTEGER default 0, 
p2 INTEGER DEFAULT NULL, p3 INTEGER DEFAULT NULL) LANGUAGE plpgsql AS $$ BEGIN insert into tab4(col1, col2, col3) select p1,p2,p3; END; $$;
CREATE OR REPLACE Procedure Pr_Test (p1 INTEGER default 0, 
p2 INTEGER DEFAULT NULL, p3 INTEGER DEFAULT NULL) LANGUAGE plpgsql SECURITY DEFINER AS $$ BEGIN insert into tab4(col1, col2, col3) select p1,p2,p3; END; $$;
CREATE or replace Procedure Pr_Test(p1 int default 0,p2 int, p3 int ) 
IS BEGIN insert into tab4 (col1, col2, col3) select p1,p2,p3 from dual; commit; insert into tab4 (col1, col2, col3) select 11110,p2,p3 from dual; rollback; END Pr_Test;
CREATE or replace Procedure Pr_Test(p1 INTEGER default 0, 
p2 INTEGER DEFAULT NULL, p3 INTEGER DEFAULT NULL) LANGUAGE plpgsql AS $$ BEGIN insert into tab4(col1, col2, col3) select p1,p2,p3; COMMIT; insert into tab4(col1, col2, col3) select 11110,p2,p3; ROLLBACK; END; $$;
CREATE or replace Procedure Pr_Test(p1 INTEGER default 0, 
p2 INTEGER DEFAULT NULL, p3 INTEGER DEFAULT NULL) LANGUAGE plpgsql SECURITY DEFINER AS $$ BEGIN insert into tab4(col1, col2, col3) select p1,p2,p3; -- COMMIT insert into tab4(col1, col2, col3) select 11110,p2,p3; -- rollback END; $$;


[Usage example]:TRIG_PROC_SCHEMA_PREFIX and TRIG_PROC_SCHEMA_SUFFIX

В приведенном ниже примере показана разница в результирующей процедуре в обоих случаях.

[POSTGRE]
TRIG_PROC_SCHEMA_PREFIX=Prefix_
TRIG_PROC_SCHEMA_SUFFIX=_Suffix

Пожалуйста, сравните:

Исходный код Informix Преобразованный код PostgreSQL (по умолчанию) Преобразованный код PostgreSQL (с установленными опциями)
create procedure schm.tr_proc_print()
  referencing new as n for my_table ;
  DEFINE v_message VARCHAR(255);
  LET v_message = 'Nailed it!';
end procedure ;

create trigger schm.tr_proc insert on schm.my_table referencing new as new
for each row
(execute procedure schm.tr_proc_print() with trigger references );
CREATE OR REPLACE FUNCTION schm.tr_proc_print_trfunc() 
RETURNS TRIGGER LANGUAGE plpgsql AS $$ DECLARE v_message VARCHAR(255); BEGIN v_message := 'Nailed it!'; RETURN NULL; END; $$; create trigger tr_proc AFTER insert on schm.my_table
for each row EXECUTE PROCEDURE schm.tr_proc_print_trfunc();
CREATE SCHEMA IF NOT EXISTS Prefix_schm_Suffix;
CREATE OR REPLACE FUNCTION Prefix_schm_Suffix.tr_proc_print_trfunc()
RETURNS TRIGGER LANGUAGE plpgsql
   AS $$
   DECLARE
   v_message VARCHAR(255);
BEGIN
   v_message := 'Nailed it!';
   RETURN NULL;
END; $$;

create trigger tr_proc AFTER insert on schm.my_table 
for each row EXECUTE PROCEDURE Prefix_schm_Suffix.tr_proc_print_trfunc();


[Usage example]:CONV_ALL_PROC_TO_FUNC

Вот пример использования опции CONV_ALL_PROC_TO_FUNC:

Исходный код MSSQL Преобразованный код PostgreSQL (по умолчанию) Преобразованный код PostgreSQL с опцией CONV_ALL_PROC_TO_FUNC =Yes
create PROCEDURE [dbo].[sp_tab_insert]
@id int,
@name varchar(10)
AS
insert into t3 values (@id, @name)
create or replace PROCEDURE sp_tab_insert(v_id INTEGER,
v_name VARCHAR)
LANGUAGE plpgsql
   AS $$
BEGIN
   insert into T3  values(v_id, v_name);
END; $$;
create or replace FUNCTION sp_tab_insert(v_id INTEGER,
v_name VARCHAR)
RETURNS VOID LANGUAGE plpgsql
   AS $$
BEGIN
   insert into T3  values(v_id, v_name);
RETURN;
END; $$;


[Usage example]:CASE_INSENS_DATA

В примере ниже показана разница в результирующей процедуре в случаях DEFAULT и COLLATION. Пожалуйста, сравните:

Тип / Значение параметра Примеры кода
Исходный код MSSQL
create table tb_collation
(
first_name varchar(64),
last_name varchar(64)
);  

create procedure pr_collation
@title varchar(20) = 'convertum'
 as
BEGIN
declare @title2 varchar(20) = 'CONVERTUM'
 IF (@title = @title2) 
 begin
 print 'equal'
 END 
 IF (@title like @title2 ) 
 begin
 print 'like'
 END 
END
PostgreSQL
CASE_INSENS_DATA=Default
create table tb_collation
(
   first_name VARCHAR(64),
   last_name VARCHAR(64)
); 

create or replace PROCEDURE pr_collation 
 (v_title VARCHAR DEFAULT 'convertum')
LANGUAGE plpgsql
   AS $$
   DECLARE
   v_title2  VARCHAR(20) DEFAULT 'CONVERTUM';
BEGIN
   IF (v_title = v_title2) then
      RAISE NOTICE 'equal';
   end if; 
   IF (v_title ilike v_title2) then
      RAISE NOTICE 'like';
   end if;
END; $$;  
PostgreSQL
CASE_INSENS_DATA=Collation
CREATE COLLATION IF NOT EXISTS swcol_ci_nondet 
 (provider = icu, locale = 'und-u-ks-level2', deterministic = false);
CREATE COLLATION IF NOT EXISTS swcol_ci_det 
 (provider = icu, locale = 'und-u-ks-level2', deterministic = true); 

create table tb_collation
(
   first_name VARCHAR(64) COLLATE swcol_ci_nondet,
   last_name VARCHAR(64) COLLATE swcol_ci_nondet ); 
 

create or replace PROCEDURE pr_collation(v_title VARCHAR DEFAULT 'ispirer')
LANGUAGE plpgsql
   AS $$
   DECLARE
   v_title2  VARCHAR(20) COLLATE swcol_ci_nondet DEFAULT 'ISPIRER';
BEGIN
   IF (v_title COLLATE swcol_ci_nondet = v_title2) then

      RAISE NOTICE 'equal';
   end if; 
   IF (v_title ilike v_title2 COLLATE swcol_ci_det) then

      RAISE NOTICE 'like';
   end if;
END; $$; 
PostgreSQL
CASE_INSENS_DATA=Lower
create table tb_collation
(
   first_name VARCHAR(64),
   last_name VARCHAR(64)
); 

create or replace PROCEDURE pr_collation(v_title VARCHAR DEFAULT 'ispirer')
LANGUAGE plpgsql
   AS $$
   DECLARE
   v_title2  VARCHAR(20) DEFAULT 'ISPIRER';
BEGIN
   IF (LOWER(v_title) = LOWER(v_title2)) then

      RAISE NOTICE 'equal';
   end if; 
   IF (v_title ilike v_title2) then

      RAISE NOTICE 'like';
   end if;
END; $$; 


Если этот вариант не сработал, пожалуйста, свяжитесь с нашей технической службой: support@convertum.ru.

[Usage example]:RESOLVE_PARAMETER_NAME_AMBIGUITY

В примере ниже показана разница в результирующей процедуре в случаях No и Yes. Пожалуйста, сравните:

Исходный код Informix PostgreSQL RESOLVE_PARAMETER_NAME_AMBIGUITY =No PostgreSQL RESOLVE_PARAMETER_NAME_AMBIGUITY =Yes
CREATE PROCEDURE sp_table_ambiguity (col1 integer, col2 integer, col3 integer)
   DEFINE var_expr1 INTEGER;
   DEFINE var_expr2 INTEGER;

   SELECT table1.col1, col3
   INTO var_expr1, var_expr2
   FROM table1
   WHERE table1.col2 = col1 LIMIT 1;
END PROCEDURE
CREATE OR REPLACE PROCEDURE sp_table_ambiguity(col1 INTEGER, col2 INTEGER, col3 INTEGER)
LANGUAGE plpgsql
   AS $$
   DECLARE
   var_expr1  INTEGER;
   var_expr2  INTEGER;
BEGIN
   SELECT table1.col1, col3
   INTO var_expr1,var_expr2
   FROM table1
   WHERE table1.col2 = col1 LIMIT 1;
END; $$; 
CREATE OR REPLACE PROCEDURE sp_table_ambiguity(col1 INTEGER, col2 INTEGER, col3 INTEGER)
LANGUAGE plpgsql
   AS $$
   DECLARE
   var_expr1  INTEGER;
   var_expr2  INTEGER;
BEGIN
   SELECT table1.col1, sp_table_ambiguity.col3
   INTO var_expr1,var_expr2
   FROM table1
   WHERE table1.col2 = sp_table_ambiguity.col1 LIMIT 1;
END; $$; 


Если этот вариант не сработал, пожалуйста, свяжитесь с нашей технической службой: support@convertum.ru.

[Usage example]:USE_CUSTOM_CAST_DATE_INT

В примере ниже показана разница в результирующей процедуре в случаях No и Yes. Пожалуйста, сравните:

Исходный код Informix PostgreSQL USE_CUSTOM_CAST_DATE_INT =No PostgreSQL USE_CUSTOM_CAST_DATE_INT =Yes
create table t_log(id integer, col1 integer, col2 integer);
CREATE PROCEDURE sp_date_as_integer (ibuf INTEGERT)
DEFINE res date;
  LET res = ibuf;
  LET ibuf = res;
  insert into t_log values (1, ibuf, res);
END PROCEDURE;
create table t_log(id INTEGER, col1 INTEGER, col2 INTEGER);
CREATE OR REPLACE PROCEDURE sp_date_as_integer(ibuf INTEGERT)
LANGUAGE plpgsql
   AS $$
   DECLARE
   res  DATE;
BEGIN
   res := CAST(ibuf AS DATE);
   ibuf := EXTRACT(DAY FROM(res):: TIMESTAMP -'1899-12-31':: TIMESTAMP);
   insert into dbm.alf_log_k  values(1, ibuf, EXTRACT(DAY FROM(res):: TIMESTAMP -'1899-12-31':: TIMESTAMP));
END; $$; 
create table t_log(id INTEGER, col1 INTEGER, col2 INTEGER);
CREATE OR REPLACE PROCEDURE sp_date_as_integer(ibuf INTEGERT)
LANGUAGE plpgsql
   AS $$
   DECLARE
   res  DATE;
BEGIN
   res := CAST(ibuf AS DATE);
   ibuf := CAST(res AS INTEGER);
   insert into t_log values(1, ibuf, CAST(res AS INTEGER));
END; $$; 


[Usage example]:ANSINULL

Ниже вы можете увидеть пример исходного кода пользовательской функции с несколькими аргументами. Левый столбец содержит исходный код, второй столбец показывает преобразованный код без опции, а третий столбец демонстрирует результаты преобразования, когда опция ANSINULL установлена в значение OFF .

Исходный код (Sybase ASE) Преобразованный код PostgreSQL (без опции ANSINULL или ANSINULL=ON) Преобразованный код PostgreSQL (с опцией ANSINULL=OFF)
 CREATE PROCEDURE sp_ansinull        
@param1 INT AS
BEGIN        
SELECT 1 
WHERE @param1=@param2
END 
 CREATE OR REPLACE FUNCTION sp_ansinull(v_param1 INTEGER, v_param2 INTEGER)
RETURNS TABLE
(
   col INTEGER
) LANGUAGE plpgsql
   AS $$
BEGIN
   IF v_param1 = v_param2 then
      return query
      select 1;
   end if;
END; $$; 
 CREATE OR REPLACE FUNCTION sp_ansinull(v_param1 INTEGER, v_param2 INTEGER)
RETURNS TABLE
(
   col INTEGER
) LANGUAGE plpgsql
   AS $$
BEGIN
   IF (v_param1 IS NOT DISTINCT FROM v_param2) then
      return query
      select 1;
   end if;
END; $$; 


Как видно, если опция ANSINULL не включена (ANSINULL=OFF), преобразование гарантирует, что поведение обработки NULL в PostgreSQL будет соответствовать поведению Sybase ASE. Это означает, что сравнения с NULL-значениями будут обрабатываться таким образом, чтобы сохранить совместимость с исходной логикой, предотвращая неожиданные результаты из-за различий в поведении базы данных.
Если опция ANSINULL включена (ANSINULL=ON), применяются стандартные правила обработки NULL в PostgreSQL, что может привести к расхождениям в обработке значений NULL при выполнении запросов.

Если этот вариант не сработал, пожалуйста, свяжитесь с нашей технической службой: support@convertum.ru.


[Usage example]:FUNC_MAX_ARGS

Ниже приведен пример исходного кода для пользовательской функции с несколькими аргументами.
Левый столбец содержит исходный код, второй столбец показывает преобразованный код без опции, а третий столбец демонстрирует результаты преобразования, когда опция FUNC_MAX_ARGS установлена в пользовательское значение.

Исходный код (Sybase ASE) Преобразованный код PostgreSQL (без опции FUNC_MAX_ARGS или FUNC_MAX_ARGS=100/Пусто) Преобразованный код PostgreSQL (с опцией FUNC_MAX_ARGS=200)
 CREATE PROCEDURE MyProc    
   @param1 INT,    
   @param2 INT,    
   @param3 INT,    
   …    
   @param200 INT
AS
BEGIN    
SELECT 1
END 
 CREATE TYPE MyProc_PS AS (
   v_param1 INTEGER,
   v_param2 INTEGER,
   v_param3 INTEGER,     
   -- Add more parameters until param200     
   v_param200 INTEGER
);

CREATE OR REPLACE FUNCTION MyProc(SWP_INPUT MyProc_PS)
RETURNS TABLE (col INTEGER) 
LANGUAGE plpgsql AS $$
BEGIN    
RETURN QUERY SELECT 1;
END; 
$$; 
 CREATE OR REPLACE FUNCTION MyProc(
   v_param1 INTEGER,
   v_param2 INTEGER,
   v_param3 INTEGER, 
   -- Add more parameters until param200    
   v_param200 INTEGER)
RETURNS TABLE (col INTEGER) 
LANGUAGE plpgsql AS $$
BEGIN    
RETURN QUERY SELECT 1;
END; 
$$; 


Как видно, если параметр FUNC_MAX_ARGS не указан, используется значение по умолчанию (100), что позволяет преобразовывать функции и процедуры с числом аргументов до 100 без создания дополнительного типа.
Если опция явно задана, то все функции/процедуры, имеющие более указанного количества параметров, будут преобразованы с использованием дополнительного типа.

Если этот вариант не сработал, пожалуйста, свяжитесь с нашей технической службой: support@convertum.ru.


[Usage example]: RECURSIVE_TRIGGERS_ENABLED

Цель этой статьи - продемонстрировать, как опция RECURSIVE_TRIGGERS_ENABLED влияет на результаты конвертации.

Следующий пример иллюстрирует влияние включения или отключения этой опции при создании триггера:
Левый столбец содержит исходный код, второй столбец показывает преобразованный код без опции, а третий столбец демонстрирует результаты преобразования, когда опция RECURSIVE_TRIGGERS_ENABLED установлена в пользовательское значение.

Исходный код (MS SQL Server) Преобразованный код PostgreSQL (без опции RECURSIVE_TRIGGERS_ENABLED илиRECURSIVE_TRIGGERS_ENABLED=No) Преобразованный код PostgreSQL (с опцией RECURSIVE_TRIGGERS_ENABLED=Yes)
create TRIGGER tr_rec_update
ON  tab_for_tr
AFTER UPDATE
AS 
BEGIN
    update t 
    set b_col = UPPER(i.b_col)
    from tab_for_tr t
    inner join inserted i on t.a_col = i.a_col
END  
CREATE TRIGGER tr_rec_update
AFTER UPDATE
ON  tab_for_tr
REFERENCING NEW TABLE AS new_table
FOR STATEMENT
WHEN(pg_trigger_depth() 
CREATE TRIGGER tr_rec_update
AFTER UPDATE
ON  tab_for_tr
REFERENCING NEW TABLE AS new_table
FOR STATEMENT
   EXECUTE PROCEDURE tr_rec_update_TrFunc(); 


Параметр RECURSIVE_TRIGGERS_ENABLED служит той же цели, что и параметр „Recursive triggers enabled“ (или RECURSIVE_TRIGGERS) в базе данных MS SQL Server. Она управляет рекурсией для триггеров. Значение по умолчанию - NO, как и OFF в MS SQL Server. Когда опция установлена в NO, все триггеры с прямой рекурсией будут вызываться один раз, а если опция установлена в YES, то рекурсия разрешена.

Если этот вариант не сработал, пожалуйста, свяжитесь с нашей технической службой: support@convertum.ru.


[Usage example]: PACKAGE_VAR_CONVERSION

Цель этой статьи - продемонстрировать, как параметр PACKAGE_VAR_CONVERSION влияет на результаты преобразования. В примерах ниже показана разница в результирующей процедуре для случаев <пусто>, pg_variables и session_config_params. Пожалуйста, сравните:

Левый столбец содержит исходный код, второй столбец показывает преобразованный код без опции, а третий столбец демонстрирует результаты преобразования, когда опция PACKAGE_VAR_CONVERSION установлена в pg_variables значение.

Тип / Значение параметра Примеры кода
Исходный код Oracle
CREATE OR REPLACE PACKAGE test_pkg1 IS
   g_n1 number := 15;
   PROCEDURE proc1;
END;
/
CREATE OR REPLACE PACKAGE BODY test_pkg1 IS
   g_n2 CONSTANT NUMBER := 2026;
   g_s  varchar2(65) := 'ТЕСТ';

   PROCEDURE print_global_vars IS
   BEGIN
      DBMS_OUTPUT.PUT_LINE('g_n1 = '||g_n1);
      DBMS_OUTPUT.PUT_LINE('g_n2 = '||g_n2);
      DBMS_OUTPUT.PUT_LINE('g_s = '||g_s);    
   END;

   PROCEDURE proc1 IS
      v_n NUMBER;
      v_v varchar2(65);
   BEGIN
      v_n := g_n2;
      if g_s = 'ТЕСТ1' then 
         g_s := 'aaa';
         g_n1 := 1000;
      else 
         g_s := 'bbb';
         g_n1 := 2000;  
      end if;
      v_v := g_s;
      DBMS_OUTPUT.PUT_LINE('v_n = '||v_n);
      DBMS_OUTPUT.PUT_LINE('v_v = '||v_v);      
      print_global_vars;
   END;
BEGIN
   DBMS_OUTPUT.PUT_LINE('Body Initialization Block');
   g_s := 'ТЕСТ1';
END;
/
PostgreSQL
PACKAGE_VAR_CONVERSION=
CREATE SCHEMA  IF NOT EXISTS TEST_PKG1
;
DROP TYPE IF EXISTS TEST_PKG1.GL_VAR_TYPE CASCADE;
CREATE type TEST_PKG1.GL_VAR_TYPE
as(g_n1 NUMERIC,
g_n2 NUMERIC,
g_s VARCHAR(65));

CREATE OR REPLACE FUNCTION TEST_PKG1.INIT_GL_VAR()
RETURNS VOID LANGUAGE plpgsql
   AS $$
   DECLARE
   SWV_GL_VAR  TEST_PKG1.GL_VAR_TYPE;
BEGIN
   CREATE TEMPORARY TABLE TEST_PKG1_GL_VAR AS SELECT(15,2026,'ТЕСТ')::TEST_PKG1.GL_VAR_TYPE AS SWV_GL_VAR_VAL;
   -- begin Initialization Block
SWV_GL_VAR := TEST_PKG1.GET_GL_VAR();
   RAISE NOTICE 'Body Initialization Block';
   SWV_GL_VAR.G_S := 'ТЕСТ1';
   CALL TEST_PKG1.SET_GL_VAR(SWV_GL_VAR);
   -- end Initialization Block
RETURN;
   EXCEPTION
   WHEN SQLSTATE '42P07' THEN
      NULL;
END; $$;

CREATE OR REPLACE FUNCTION TEST_PKG1.GET_GL_VAR()
RETURNS TEST_PKG1.GL_VAR_TYPE LANGUAGE plpgsql
   AS $$
   DECLARE
   SWV_GL_VAR  TEST_PKG1.GL_VAR_TYPE;
BEGIN
   RETURN(select SWV_GL_VAR_VAL:: TEST_PKG1.GL_VAR_TYPE from TEST_PKG1_GL_VAR);
   EXCEPTION
   WHEN OTHERS THEN
      PERFORM TEST_PKG1.INIT_GL_VAR();
      RETURN(select SWV_GL_VAR_VAL:: TEST_PKG1.GL_VAR_TYPE from TEST_PKG1_GL_VAR);
END; $$;

CREATE OR REPLACE PROCEDURE TEST_PKG1.SET_GL_VAR(SWP_GLVAR TEST_PKG1.GL_VAR_TYPE)
LANGUAGE plpgsql
   AS $$
BEGIN
   UPDATE TEST_PKG1_GL_VAR SET SWV_GL_VAR_VAL = SWP_GLVAR;
END; $$;

CREATE OR REPLACE PROCEDURE test_pkg1.PRINT_GLOBAL_VARS()
LANGUAGE plpgsql
   AS $$
   DECLARE
   SWV_GL_VAR  TEST_PKG1.GL_VAR_TYPE DEFAULT TEST_PKG1.GET_GL_VAR();
BEGIN
   RAISE NOTICE '%',CONCAT('g_n1 = ',SWV_GL_VAR.g_n1);
   RAISE NOTICE '%',CONCAT('g_n2 = ',SWV_GL_VAR.g_n2);
   RAISE NOTICE '%',CONCAT('g_s = ',SWV_GL_VAR.g_s);
END; $$;

CREATE OR REPLACE PROCEDURE test_pkg1.PROC1()
LANGUAGE plpgsql
   AS $$
   DECLARE
   SWV_GL_VAR  TEST_PKG1.GL_VAR_TYPE DEFAULT TEST_PKG1.GET_GL_VAR();
   v_n  NUMERIC;
   v_v  VARCHAR(65);
BEGIN
   v_n := SWV_GL_VAR.g_n2;
   if SWV_GL_VAR.g_s = 'ТЕСТ1' then
      SWV_GL_VAR.G_S := 'aaa';
      CALL TEST_PKG1.SET_GL_VAR(SWV_GL_VAR);
      SWV_GL_VAR.G_N1 := 1000;
      CALL TEST_PKG1.SET_GL_VAR(SWV_GL_VAR);
   else
      SWV_GL_VAR.G_S := 'bbb';
      CALL TEST_PKG1.SET_GL_VAR(SWV_GL_VAR);
      SWV_GL_VAR.G_N1 := 2000;
      CALL TEST_PKG1.SET_GL_VAR(SWV_GL_VAR);
   end if;
   v_v := SWV_GL_VAR.g_s;
   RAISE NOTICE '%',CONCAT('v_n = ',v_n);
   RAISE NOTICE '%',CONCAT('v_v = ',v_v);      
   CALL TEST_PKG1.PRINT_GLOBAL_VARS();
   SWV_GL_VAR := TEST_PKG1.GET_GL_VAR();
END; $$; 
PostgreSQL
PACKAGE_VAR_CONVERSION=pg_variables
CREATE SCHEMA  IF NOT EXISTS TEST_PKG1
;
DROP TYPE IF EXISTS TEST_PKG1.GL_VAR_TYPE CASCADE;
CREATE type TEST_PKG1.GL_VAR_TYPE
as(g_n1 NUMERIC,
g_n2 NUMERIC,
g_s VARCHAR(65));

CREATE OR REPLACE FUNCTION TEST_PKG1.GET_GL_VAR()
RETURNS TEST_PKG1.GL_VAR_TYPE LANGUAGE plpgsql
   AS $$
   DECLARE
   SWV_GL_VAR  TEST_PKG1.GL_VAR_TYPE;
BEGIN
   if not pgv_exists('TEST_PKG1','GL_VAR_TYPE') then
      perform pgv_set('TEST_PKG1','GL_VAR_TYPE',(15,2026,'ТЕСТ')::TEST_PKG1.GL_VAR_TYPE);
      -- begin Initialization Block
      SWV_GL_VAR := TEST_PKG1.GET_GL_VAR();
      RAISE NOTICE 'Body Initialization Block';
      SWV_GL_VAR.G_S := 'ТЕСТ1';
      PERFORM TEST_PKG1.SET_GL_VAR(SWV_GL_VAR);
      -- end Initialization Block
end if;
   return(select pgv_get('TEST_PKG1','GL_VAR_TYPE',NULL:: TEST_PKG1.GL_VAR_TYPE));
END; $$;

CREATE OR REPLACE FUNCTION TEST_PKG1.SET_GL_VAR(SWP_GLVAR TEST_PKG1.GL_VAR_TYPE)
RETURNS VOID LANGUAGE plpgsql
   AS $$
BEGIN
   perform pgv_set('TEST_PKG1','GL_VAR_TYPE',SWP_GLVAR);
END; $$;

CREATE OR REPLACE PROCEDURE test_pkg1.PRINT_GLOBAL_VARS()
LANGUAGE plpgsql
   AS $$
   DECLARE
   SWV_GL_VAR  TEST_PKG1.GL_VAR_TYPE DEFAULT TEST_PKG1.GET_GL_VAR();
BEGIN
   RAISE NOTICE '%',CONCAT('g_n1 = ',SWV_GL_VAR.g_n1);
   RAISE NOTICE '%',CONCAT('g_n2 = ',SWV_GL_VAR.g_n2);
   RAISE NOTICE '%',CONCAT('g_s = ',SWV_GL_VAR.g_s);
END; $$;

CREATE OR REPLACE PROCEDURE test_pkg1.PROC1()
LANGUAGE plpgsql
   AS $$
   DECLARE
   SWV_GL_VAR  TEST_PKG1.GL_VAR_TYPE DEFAULT TEST_PKG1.GET_GL_VAR();
   v_n  NUMERIC;
   v_v  VARCHAR(65);
BEGIN
   v_n := SWV_GL_VAR.g_n2;
   if SWV_GL_VAR.g_s = 'ТЕСТ1' then
      SWV_GL_VAR.G_S := 'aaa';
      PERFORM TEST_PKG1.SET_GL_VAR(SWV_GL_VAR);
      SWV_GL_VAR.G_N1 := 1000;
      PERFORM TEST_PKG1.SET_GL_VAR(SWV_GL_VAR);
   else
      SWV_GL_VAR.G_S := 'bbb';
      PERFORM TEST_PKG1.SET_GL_VAR(SWV_GL_VAR);
      SWV_GL_VAR.G_N1 := 2000;
      PERFORM TEST_PKG1.SET_GL_VAR(SWV_GL_VAR);
   end if;
   v_v := SWV_GL_VAR.g_s;
   RAISE NOTICE '%',CONCAT('v_n = ',v_n);
   RAISE NOTICE '%',CONCAT('v_v = ',v_v);      
   CALL TEST_PKG1.PRINT_GLOBAL_VARS();
   SWV_GL_VAR := TEST_PKG1.GET_GL_VAR();
END; $$; 
PostgreSQL
PACKAGE_VAR_CONVERSION= session_config_params
CREATE SCHEMA  IF NOT EXISTS TEST_PKG1
;
DROP TYPE IF EXISTS TEST_PKG1.GL_VAR_TYPE CASCADE;
CREATE type TEST_PKG1.GL_VAR_TYPE
as(g_n1 NUMERIC,
g_n2 NUMERIC,
g_s VARCHAR(65));

CREATE OR REPLACE FUNCTION TEST_PKG1.GET_GL_VAR()
RETURNS TEST_PKG1.GL_VAR_TYPE LANGUAGE plpgsql
   AS $$
   DECLARE
   SWV_GL_VAR  TEST_PKG1.GL_VAR_TYPE;
BEGIN
   RETURN current_setting('sqlways.TEST_PKG1_GL_VAR'):: TEST_PKG1.GL_VAR_TYPE;
   EXCEPTION
   WHEN SQLSTATE '42704' THEN
      SET sqlways.TEST_PKG1_GL_VAR = DEFAULT;
      PERFORM set_config('sqlways.TEST_PKG1_GL_VAR',(15,2026,'ТЕСТ')::TEST_PKG1.GL_VAR_TYPE::text,
      false);
      -- begin Initialization Block
      SWV_GL_VAR := TEST_PKG1.GET_GL_VAR();
      RAISE NOTICE 'Body Initialization Block';
      SWV_GL_VAR.G_S := 'ТЕСТ1';
      PERFORM TEST_PKG1.SET_GL_VAR(SWV_GL_VAR);
      -- end Initialization Block
   RETURN current_setting('sqlways.TEST_PKG1_GL_VAR'):: TEST_PKG1.GL_VAR_TYPE;
END; $$;

CREATE OR REPLACE FUNCTION TEST_PKG1.SET_GL_VAR(SWP_GLVAR TEST_PKG1.GL_VAR_TYPE)
RETURNS VOID LANGUAGE plpgsql
   AS $$
BEGIN
   perform set_config('sqlways.TEST_PKG1_GL_VAR',SWP_GLVAR::text,false);
END; $$;

CREATE OR REPLACE PROCEDURE test_pkg1.PRINT_GLOBAL_VARS()
LANGUAGE plpgsql
   AS $$
   DECLARE
   SWV_GL_VAR  TEST_PKG1.GL_VAR_TYPE DEFAULT TEST_PKG1.GET_GL_VAR();
BEGIN
   RAISE NOTICE '%',CONCAT('g_n1 = ',SWV_GL_VAR.g_n1);
   RAISE NOTICE '%',CONCAT('g_n2 = ',SWV_GL_VAR.g_n2);
   RAISE NOTICE '%',CONCAT('g_s = ',SWV_GL_VAR.g_s);
END; $$;

CREATE OR REPLACE PROCEDURE test_pkg1.PROC1()
LANGUAGE plpgsql
   AS $$
   DECLARE
   SWV_GL_VAR  TEST_PKG1.GL_VAR_TYPE DEFAULT TEST_PKG1.GET_GL_VAR();
   v_n  NUMERIC;
   v_v  VARCHAR(65);
BEGIN
   v_n := SWV_GL_VAR.g_n2;
   if SWV_GL_VAR.g_s = 'ТЕСТ1' then
      SWV_GL_VAR.G_S := 'aaa';
      PERFORM TEST_PKG1.SET_GL_VAR(SWV_GL_VAR);
      SWV_GL_VAR.G_N1 := 1000;
      PERFORM TEST_PKG1.SET_GL_VAR(SWV_GL_VAR);
   else
      SWV_GL_VAR.G_S := 'bbb';
      PERFORM TEST_PKG1.SET_GL_VAR(SWV_GL_VAR);
      SWV_GL_VAR.G_N1 := 2000;
      PERFORM TEST_PKG1.SET_GL_VAR(SWV_GL_VAR);
   end if;
   v_v := SWV_GL_VAR.g_s;
   RAISE NOTICE '%',CONCAT('v_n = ',v_n);
   RAISE NOTICE '%',CONCAT('v_v = ',v_v);      
   CALL TEST_PKG1.PRINT_GLOBAL_VARS();
   SWV_GL_VAR := TEST_PKG1.GET_GL_VAR();
END; $$; 


Если этот вариант не сработал, пожалуйста, свяжитесь с нашей технической службой: support@convertum.ru.

[Usage example]: AUTO_FUNCTION_VOLATILITY

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

Исходный код (Informix) Преобразованный код PostgreSQL (без опции AUTO_FUNCTION_VOLATILITY или AUTO_FUNCTION_VOLATILITY=No) Преобразованный код PostgreSQL (с опцией AUTO_FUNCTION_VOLATILITY=Yes)
create function fn_immutable (param int)
returning int;
   return param * 5;
end function;  

create function fn_stable (param int)
returning date, char(30);
   return current year to day, 'The best day';
end function; 
create or replace function fn_immutable(param integer)
returns integer language plpgsql
   as $$
begin
   return param*5;
end; $$;  

create or replace function fn_stable(param integer)
returns table
(
   unnamed_col_1 date,
   unnamed_col_2 char(30)
) language plpgsql
   as $$
begin
   return query(select current_date,'The best day':: char(30));
end; $$; 
create or replace function fn_immutable(param integer)
returns integer IMMUTABLE language plpgsql
   as $$
begin
   return param*5;
end; $$;  

create or replace function fn_stable(param integer)
returns table
(
   unnamed_col_1 date,
   unnamed_col_2 char(30)
) STABLE language plpgsql
   as $$
begin
   return query(select current_date,'The best day':: char(30));
end; $$; 


Если этот вариант не сработал, пожалуйста, свяжитесь с нашей технической службой: support@convertum.ru.


[Usage example]: EXPLICIT_COLUMN_OWNERSHIP

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

Исходный код (Infromix) Преобразованный код PostgreSQL (без опции EXPLICIT_COLUMN_OWNERSHIP или EXPLICIT_COLUMN_OWNERSHIP=No) Преобразованный код PostgreSQL (с опцией EXPLICIT_COLUMN_OWNERSHIP=Yes)
CREATE PROCEDURE sp_1 (par1 integer)
   DEFINE var1, var2 INTEGER;

   SELECT col1t1, col1t2
   INTO var1, var2
   FROM table1, OUTER table2
   WHERE 
   col1t1 = col1t2 AND
   col2t1 = par1 
LIMIT 1;
END PROCEDURE 
CREATE OR REPLACE PROCEDURE sp_1(par1 INTEGER)
LANGUAGE plpgsql
   AS $$
   DECLARE
   var1  INTEGER;
   var2  INTEGER;
BEGIN
   SELECT col1t1, col1t2
   INTO var1,var2
   FROM table1
   LEFT OUTER JOIN table2
   ON col1t1 = col1t2
   WHERE
   col2t1 = par1
   LIMIT 1;
END; $$; 
CREATE OR REPLACE PROCEDURE sp_1(par1 INTEGER)
LANGUAGE plpgsql
   AS $$
   DECLARE
   var1  INTEGER;
   var2  INTEGER;
BEGIN
   SELECT table1.col1t1, table2.col1t2
   INTO var1,var2
   FROM table1
   LEFT OUTER JOIN table2
   ON table1.col1t1 = table2.col1t2
   WHERE
   table1.col2t1 = par1
   LIMIT 1;
END; $$; 


Если этот вариант не сработал, пожалуйста, свяжитесь с нашей технической службой: support@convertum.ru.


[Usage example]: ANALYZE_TEMP_STATS

Ниже вы можете увидеть пример исходного кода с временными таблицами и индексами. Левый столбец содержит исходный код Sybase ASE, второй столбец показывает преобразованный код PostgreSQL без опции, а третий столбец демонстрирует результаты преобразования, когда включена опция ANALYZE_TEMP_STATS.

Исходный код Sybase ASE

CREATE PROCEDURE test_proc
AS
BEGIN
    CREATE TABLE #temp3 (id INT, name VARCHAR(50))

    CREATE INDEX idx_temp3_id ON #temp3 (id)

    INSERT INTO #temp3     
    SELECT id, name
    FROM regular_table
    WHERE status = 'ACTIVE'

    INSERT INTO #temp3 VALUES (101, 'AnotherRow')

    INSERT INTO regular_table
    SELECT id, name, 'Val6', 'ACTIVE'
    FROM #temp3
    WHERE id = 1

    CREATE INDEX idx_temp3_name ON #temp3 (name)
    CREATE UNIQUE INDEX idx_temp3_name ON #temp3 (name)

    INSERT INTO #temp3 VALUES (101, 'AnotherRow')
    INSERT INTO #temp3 VALUES (102, 'AnotherRow')
    INSERT INTO #temp3 VALUES (103, 'AnotherRow')
END

Преобразованный код PostgreSQL (по умолчанию)

CREATE OR REPLACE FUNCTION test_proc()
RETURNS TABLE
(
   id INTEGER,
   name VARCHAR
) LANGUAGE plpgsql
   AS $$
BEGIN
   CREATE TEMPORARY TABLE tt_TEMP3 
   (
      id INTEGER, 
      name VARCHAR(50)
   ) ON COMMIT DROP;

   CREATE INDEX idx_temp3_id ON tt_TEMP3 
   (id);

   INSERT INTO tt_TEMP3
   SELECT id, name
   FROM regular_table
   WHERE status = 'ACTIVE';

   INSERT INTO tt_TEMP3  VALUES(101, 'AnotherRow');

   INSERT INTO regular_table
   SELECT id, name, 'Val6', 'ACTIVE'
   FROM tt_TEMP3
   WHERE id = 1;

   CREATE INDEX idx_temp3_name ON tt_TEMP3 
   (name);
   CREATE UNIQUE INDEX idx_temp3_name ON tt_TEMP3 
   (name);

   INSERT INTO tt_TEMP3  VALUES(101, 'AnotherRow');
   INSERT INTO tt_TEMP3  VALUES(102, 'AnotherRow');
   INSERT INTO tt_TEMP3  VALUES(103, 'AnotherRow');
RETURN;
END; $$;

Преобразованный код PostgreSQL с опцией ANALYZE_TEMP_STATS=Yes

CREATE OR REPLACE FUNCTION test_proc()
RETURNS TABLE
(
   id INTEGER,
   name VARCHAR
) LANGUAGE plpgsql
   AS $$
BEGIN
   CREATE TEMPORARY TABLE tt_TEMP3 
   (
      id INTEGER, 
      name VARCHAR(50)
   ) ON COMMIT DROP;

   CREATE INDEX idx_temp3_id ON tt_TEMP3 
   (id);
   ANALYZE tt_TEMP3;

   INSERT INTO tt_TEMP3
   SELECT id, name
   FROM regular_table
   WHERE status = 'ACTIVE';

   INSERT INTO tt_TEMP3  VALUES(101, 'AnotherRow');

   ANALYZE tt_TEMP3;   

   INSERT INTO regular_table
   SELECT id, name, 'Val6', 'ACTIVE'
   FROM tt_TEMP3
   WHERE id = 1;

   CREATE INDEX idx_temp3_name ON tt_TEMP3 
   (name);
   CREATE UNIQUE INDEX idx_temp3_name ON tt_TEMP3 
   (name);
   ANALYZE tt_TEMP3;

   INSERT INTO tt_TEMP3  VALUES(101, 'AnotherRow');
   INSERT INTO tt_TEMP3  VALUES(102, 'AnotherRow');
   INSERT INTO tt_TEMP3  VALUES(103, 'AnotherRow');
   ANALYZE tt_TEMP3;
RETURN;
END; $$;

Если параметр ANALYZE_TEMP_STATS не указан, используется значение по умолчанию (No), что означает, что в процессе преобразования не добавляются дополнительные инструкции ANALYZE.
Если опция включена явно (Yes), то инструкции ANALYZE будут автоматически вставляться после создания индекса и операций INSERT во временных таблицах, гарантируя, что оптимизатор PostgreSQL будет работать со свежей статистикой при выполнении последующих запросов.

Если этот вариант не сработал, пожалуйста, свяжитесь с нашей технической службой: support@convertum.ru.