【问题标题】:Porting from Oracle to Postgres从 Oracle 移植到 Postgres
【发布时间】:2015-10-06 10:41:25
【问题描述】:

以前我们的托管环境是在 Oracle 上,但现在我们最近切换到了 postgres。我认为移植表是成功的,因为它已经正常工作了很长时间。我实际上一直在努力移植一个程序,但由于某种原因没有工作。我浏览了 ora2pg 的文档部分,但我无法破解,我相信这是最后一块拼图。

我从这个开始,它在 Oracle 中看起来像这样:

create or replace
procedure c_audit(anonymous in boolean, aud_level IN varchar)
AUTHID CURRENT_USER IS 

  script varchar2(32000);
  acc_select varchar(100);
  open_cursor integer;
  returnval integer;
  p_id integer;
  a_id integer;
  p_name varchar2(100);
  a_name varchar2(100);
  v_count integer;
  c_count integer;
  doc_count integer;
  curr_user varchar(100);
begin

for i in (select a.a_name a_name, a.a_id, p.name p_name, p.id p_id, ds.username username
from c_account a
inner join c_pro p on a.a_id = p.a_id
inner join c_dat_ds_xref x on p.id = x.p_id
inner join c_data ds on x.id_datasource = ds.id
inner join c_conntypes ct on x.id_conntype = ct.id_conntype
where ct.typeid = 'CAPTURE'
order by a.a_name, p.name)
LOOP
    curr_user := i.username;
    IF anonymous = true
    THEN
        acc_select := 'select ' || '''' || i.a_id || '''' || ' a_id,' || '''' || i.p_id || '''' || ' p_id';

    ELSE
        acc_select := 'select ' || '''' || i.a_name || '''' || ' a_name,' || '''' || i.p_name || '''' || ' p_name';
    END IF;

    IF upper(aud_level) = 'VERBATIM'
    THEN         
          script:= acc_select || '
        , count(distinct d.document_id) docCount 
        , sum(case when v.document_id is null or v.verbatim_type_value = ''NO_VERBATIM_TEXT'' then 0 else 1 end) VerbCount 
        , sum(case when v.document_id is null then (select to_number(prop_value) verbSize 
                                        from c_properties 
                                        where prop_name = ''METERING.STRUCT.ONLY.CHARGE'' 
                                        and id_pro = 0) else v.credits end) CreditCount 
        from ' || i.username || '.p_document d 
        left outer join ( 
                        select vi.document_id, t.verbatim_type_value 
                        , case when dbms_lob.substr(vi.extracted_original,8) = ''<cbnull>''
                          or t.verbatim_type_value = ''NO_VERBATIM_TEXT''
                                        then coalesce(s2.strucCredit, .25)                            
                                        else ceil(vi.extracted_original_size/coalesce(s.verbSize, 2048)) end credits                          
                        from ' || i.username || '.p_verbatim vi 
                        left outer join ' || i.username || '.pd_verbatim_type t on vi.verbatim_type_id = t.verbatim_type_id
                        , ( 
                                        select to_number(prop_value) verbSize 
                                        from c_properties 
                                        where prop_name = ''METERING.MAXSIZE.VERBATIM'' 
                                        and id_pro = 0 
                        ) s 
                        , ( 
                                        select to_number(prop_value) strucCredit 
                                        from c_properties 
                                        where prop_name = ''METERING.STRUCT.ONLY.CHARGE'' 
                                        and id_pro = 0 
                        ) s2 
        ) v on d.document_id = v.document_id';
    ELSE    
        IF upper(aud_level) = 'DOCUMENT'
        THEN
            script:= acc_select || '
            , count(distinct a.document_id) docCount
            , sum(credits) creditCount
            from (
            select d.document_id, ceil(sum(v.extracted_original_size)/coalesce(s.verbSize,2048)) credits
            from ' || i.username || '.p_document d
            inner join ' || i.username || '.p_verbatim v on d.document_id = v.document_id
            inner join ' || i.username || '.pd_verbatim_type t on v.verbatim_type_id = t.verbatim_type_id
            , ( 
                            select to_number(prop_value) verbSize 
                            from c_properties 
                            where prop_name = ''METERING.MAXSIZE.VERBATIM'' 
                            and id_pro = 0 
            ) s 
            where t.verbatim_type_value <> ''NO_VERBATIM_TEXT''
            and dbms_lob.substr(v.extracted_original,8) <> ''<cbnull>''
            group by d.document_id, s.verbSize
            union
            select d.document_id, coalesce(s2.strucCredit, .25)
            from ' || i.username || '.p_document d
            , ( 
                            select to_number(prop_value) strucCredit 
                            from c_properties 
                            where prop_name = ''METERING.STRUCT.ONLY.CHARGE'' 
                            and id_pro = 0 
            ) s2 
            where d.document_id not in (select distinct v.document_id from ' || i.username || '.p_verbatim v)
            union 
            select distinct d.document_id, coalesce(s2.strucCredit, .25)
            from ' || i.username || '.p_document d
            inner join ' || i.username || '.p_verbatim v on d.document_id = v.document_id
            inner join ' || i.username || '.pd_verbatim_type t on v.verbatim_type_id = t.verbatim_type_id
            , ( 
                            select to_number(prop_value) strucCredit 
                            from c_properties 
                            where prop_name = ''METERING.STRUCT.ONLY.CHARGE'' 
                            and id_pro = 0 
            ) s2 
            where (t.verbatim_type_value = ''NO_VERBATIM_TEXT''
            or dbms_lob.substr(v.extracted_original,8) = ''<cbnull>'')
            ) a';
            ELSE
                dbms_output.put_line('Invalid choice for audit level, no audit generated');
                exit;
            END IF;
    END IF;
begin

open_cursor := dbms_sql.open_cursor;
        DBMS_SQL.PARSE(open_cursor, script,
                   DBMS_SQL.NATIVE);
        IF anonymous = true then
            dbms_sql.define_column(open_cursor,1,a_id);
            dbms_sql.define_column(open_cursor,2,p_id);
        else
            dbms_sql.define_column(open_cursor,1,a_name,100);
            dbms_sql.define_column(open_cursor,2,p_name,100);
        end if;
        dbms_sql.define_column(open_cursor,3,doc_count);
        dbms_sql.define_column(open_cursor,4,v_count);
        IF upper(aud_level) = 'VERBATIM' then
            dbms_sql.define_column(open_cursor,5,c_count);
        end if;
        returnval := DBMS_SQL.EXECUTE(open_cursor);
        loop
            if dbms_sql.fetch_rows(open_cursor) > 0 then
                IF anonymous = true then
                    dbms_sql.column_value(open_cursor,1,a_id);
                    dbms_sql.column_value(open_cursor,2,p_id);
                    dbms_sql.column_value(open_cursor,3,doc_count);
                    dbms_sql.column_value(open_cursor,4,v_count);
                    IF upper(aud_level) = 'VERBATIM' then
                        dbms_sql.column_value(open_cursor,5,c_count);
                        dbms_output.put_line(a_id || ',' || p_id || ',' || doc_count || ',' || v_count || ',' || c_count);
                    else
                        dbms_output.put_line(a_id || ',' || p_id || ',' || doc_count || ',' || v_count);
                    end if;
                else
                    dbms_sql.column_value(open_cursor,1,a_name);
                    dbms_sql.column_value(open_cursor,2,p_name);
                    dbms_sql.column_value(open_cursor,3,doc_count);
                    dbms_sql.column_value(open_cursor,4,v_count);
                    IF upper(aud_level) = 'VERBATIM' then
                        dbms_sql.column_value(open_cursor,5,c_count);
                        dbms_output.put_line(a_name || ',' || p_name || ',' || doc_count || ',' || v_count || ',' || c_count);
                    else
                        dbms_output.put_line(a_name || ',' || p_name || ',' || doc_count || ',' || v_count);
                    end if;
                end if;
            else
                exit;
            end if;
        end loop;

        exception 
            when others then
            --dbms_output.put_line('Error occured. Please check if the current user has Select access to table ' || curr_user || '.p_document ' || curr_user || '.p_verbatim ' || curr_user || '.pd_verbatim_type');
      dbms_output.put_line('Error occured. Please login as ' || curr_user || ' and run the following:');
      dbms_output.put_line('GRANT SELECT ON ' || curr_user || '.P_DOCUMENT to ' || user ||';');
      dbms_output.put_line('GRANT SELECT ON ' || curr_user || '.P_VERBATIM to ' || user ||';');
      dbms_output.put_line('GRANT SELECT ON ' || curr_user || '.pd_verbatim_type to ' || user ||';');
end;
end loop;
end;

这个过程在语法方面看起来正确吗?

CREATE OR REPLACE FUNCTION c_audit(anonymous boolean, aud_level text) RETURNS VOID AS $body$
DECLARE
  script text;
  acc_select text;
  returnval integer;
  p_id integer;
  a_id integer;
  i record;
  p_name text;
  a_name text;
  v_count integer;
  c_count integer;
  doc_count integer;
  curr_user text;
BEGIN
for i in (SELECT a.a_name a_name, a.a_id, p.name p_name, p.id p_id, ds.username username
from c_account a
inner join c_pro p on a.a_id = p.a_id
inner join c_dat_ds_xref x on p.id = x.p_id
inner join c_data ds on x.id_datasource = ds.id
inner join c_conntypes ct on x.id_conntype = ct.id_conntype
where ct.typeid = 'CAPTURE'
order by a.a_name, p.name)
LOOP
    curr_user := i.username;
    IF anonymous = true
    THEN
        acc_select := 'SELECT ' || '''' || i.a_id || '''' || ' AccountID,' || '''' || i.p_id || '''' || ' ProjectID';
    ELSE
        acc_select := 'SELECT ' || '''' || i.a_name || '''' || ' AccountName,' || '''' || i.p_name || '''' || ' ProjectName';
    END IF;
    IF upper(aud_level) = 'VERBATIM' 
    THEN         
          script:= acc_select || '
        , count(distinct d.document_id) docCount 
        , sum(case when coalesce(CAST(v.document_id AS text), '') = '' or v.verbatim_type_value = ''NO_VERBATIM_TEXT'' then 0 else 1 end) VerbCount 
        , sum(case when coalesce(CAST(v.document_id AS text), '') = '' then (SELECT to_number(prop_value,''9999.99'') verbSize 
                                        from c_properties 
                                        where prop_name = ''METERING.STRUCT.ONLY.CHARGE'' 
                                        and id_project = 0) else v.credits end) CreditCount 
        from ' || i.username || '.p_document d 
        left outer join ( 
                        SELECT vi.document_id, t.verbatim_type_value 
                        , case when substr(vi.extracted_original,8) = ''<cbnull>''
                          or t.verbatim_type_value = ''NO_VERBATIM_TEXT''
                                        then coalesce(s2.strucCredit, .25)                            
                                        else ceil(vi.extracted_original_size/coalesce(s.verbSize, 2048)) end credits                          
                        from ' || i.username || '.p_verbatim vi 
                        left outer join ' || i.username || '.pd_verbatim_type t on vi.verbatim_type_id = t.verbatim_type_id
                        , ( 
                                        select to_number(prop_value,''9999.99'') verbSize 
                                        from c_properties 
                                        where prop_name = ''METERING.MAXSIZE.VERBATIM'' 
                                        and id_project = 0 
                        ) s 
                        , ( 
                                        select to_number(prop_value,''9999.99'') strucCredit 
                                        from c_properties 
                                        where prop_name = ''METERING.STRUCT.ONLY.CHARGE'' 
                                        and id_project = 0 
                        ) s2 
        ) v on d.document_id = v.document_id';
        SELECT format(script) into script;
    ELSE IF upper(aud_level) = 'DOCUMENT'
        THEN
            script:= acc_select || '
            , count(distinct a.document_id) docCount
            , sum(credits) creditCount
            from (
            SELECT d.document_id, ceil(sum(v.extracted_original_size)/coalesce(s.verbSize,2048)) credits
            from ' || i.username || '.p_document d
            inner join ' || i.username || '.p_verbatim v on d.document_id = v.document_id
            inner join ' || i.username || '.pd_verbatim_type t on v.verbatim_type_id = t.verbatim_type_id
            , ( 
                            SELECT to_number(prop_value,''9999.99'') verbSize 
                            from c_properties 
                            where prop_name = ''METERING.MAXSIZE.VERBATIM'' 
                            and id_project = 0 
            ) s 
            where t.verbatim_type_value <> ''NO_VERBATIM_TEXT''
            and substr(v.extracted_original,8) <> ''<cbnull>''
            group by d.document_id, s.verbSize
            union
            select d.document_id, coalesce(s2.strucCredit, .25)
            from ' || i.username || '.p_document d
            , ( 
                            select to_number(prop_value,''9999.99'') strucCredit 
                            from c_properties 
                            where prop_name = ''METERING.STRUCT.ONLY.CHARGE'' 
                            and id_project = 0 
            ) s2 
            where d.document_id not in (select distinct v.document_id from ' || i.username || '.p_verbatim v)
            union 
            select distinct d.document_id, coalesce(s2.strucCredit, .25)
            from ' || i.username || '.p_document d
            inner join ' || i.username || '.p_verbatim v on d.document_id = v.document_id
            inner join ' || i.username || '.pd_verbatim_type t on v.verbatim_type_id = t.verbatim_type_id
            , ( 
                            select to_number(prop_value,''9999.99'') strucCredit 
                            from c_properties 
                            where prop_name = ''METERING.STRUCT.ONLY.CHARGE'' 
                            and id_project = 0 
            ) s2 
            where (t.verbatim_type_value = ''NO_VERBATIM_TEXT''
            or substr(v.extracted_original,8) = ''<cbnull>'')
            ) a';
            SELECT format(script) into script;
        ELSE
        SELECT format(script) into script;
        exit;
        END IF;
    END IF;
BEGIN
    IF anonymous = true 
    THEN
        IF upper(aud_level) = 'VERBATIM' 
        THEN
            EXECUTE script into a_id, p_id, doc_count, v_count, c_count;
        ELSE
            EXECUTE script into a_id, p_id, doc_count, c_count;
        END IF;
    ELSE
        IF upper(aud_level) = 'VERBATIM'
        THEN
            EXECUTE script into a_name, p_name, doc_count, v_count, c_count;
        ELSE
            EXECUTE script into a_name, p_name, doc_count, c_count;
        END IF;
    END IF;
    GET DIAGNOSTICS returnval := ROW_COUNT;
        LOOP
            IF returnval > 0
            THEN
                IF anonymous = true
                THEN
                    IF upper(aud_level) = 'VERBATIM'
                    THEN
                        SELECT format ('Information %s, %s, %s, %s, %s', a_id, p_id, doc_count, v_count, c_count);
                    ELSE
                        SELECT format ('Information %s, %s, %s, %s', a_id, p_id, doc_count, c_count);
                    END IF;
                ELSE
                    IF upper(aud_level) = 'VERBATIM'
                    THEN
                        SELECT format ('Information %s, %s, %s, %s, %s', a_name, p_name, doc_count, v_count, c_count);
                    ELSE
                        SELECT format ('Information %s, %s, %s, %s', a_name, p_name, doc_count, c_count);
                    END IF;
                END IF;
            ELSE
                EXIT;
            END IF;
        END LOOP;
    EXCEPTION
        WHEN others THEN
          PERFORM format('Error occured. Please login as %s, %s' ,  curr_user ,  ' and run the following:');
          PERFORM format('GRANT SELECT ON %s.P_DOCUMENT to %s', curr_user, user);
          PERFORM format('GRANT SELECT ON %s.P_VERBATIM to %s', curr_user, user);
          PERFORM format('GRANT SELECT ON %s.pd_verbatim_type to %s', curr_user, user);
END;
END LOOP;
END;
$body$
LANGUAGE PLPGSQL
;
ALTER FUNCTION cb_audit(boolean, text) OWNER TO USER;
-- REVOKE ALL ON FUNCTION cb_audit FROM PUBLIC;

我得到的失败错误是 -

错误:为 RAISE 指定的参数过多 其中: PL/pgSQL 函数“cb_audit”在 RAISE 的第 145 行

我发现这是一个很好的链接,我将其用作reference

我相信对于移植 DBMS_OUTPUT.PUT_LINE,RAISE NOTICE 应该是正确的方法。我遇到了另一个错误,它是 -to_number(prop_value,'9999.99') 的格式,它按照here 提到的语法显示是正确的,但是由于某种原因,当我切换到 to_number(prop_value,''9999.99'') ,我没有收到错误,但不确定为什么会这样,或者即使它应该正常工作。

Postgres 的版本 -

x86_64-unknown-linux-gnu 上的 PostgreSQL 9.1.10,由 gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-3) 编译,64 位

已编辑: 我实际上尝试根据 Patrick 的建议修改此功能,但由于某种原因,它没有在屏幕上显示任何内容。我在每个脚本的末尾添加了 format() 以显示脚本,但它只是执行代码并显示 c_audit 和 null。尽管我执行了各个 subsql,但它们确实返回了预期的计数和结果。我错过了什么吗?

【问题讨论】:

    标签: oracle postgresql


    【解决方案1】:

    您正在使用两种类型的字符串“building”:串联|| 运算符和% 占位符。两者都是完全合法的,但首选使用format() function,因为代码更简洁,PG 在幕后防范SQL-injection 之类的东西。

    您得到的错误是您的RAISE NOTICE 命令之一有多个% 与您提供的参数不同;请原谅我没有查到,但是在 SO 上很难找到 line 145。一般来说,你应该像这样重写它们:

    RAISE NOTICE format('GRANT SELECT ON %I.pd_verbatim_type to %I', curr_user, user);
    

    %I 占位符将 SQL 标识符作为输入:它不能是 NULL,它将被正确引用以避免 SQL 注入和关键字冲突。 (根据您的代码,我认为 curr_user 是架构名称,user 是角色名称,都是 SQL 标识符。)

    还要注意,PL/pgSQL 函数中的PERFORM 语句是SELECT 语句,它不返回数据,但执行它是为了检查副作用,例如断言某些数据存在。因此,没有PERFORM 权限,请改用GRANT SELECT

    在 PostgreSQL 中引用时,SQL 标识符使用双引号,而字符串文字值使用单引号。 to_number() 函数肯定需要单引号。

    还有几点可以改进您的代码:

    (1)动态查询中的两个sub-selects这样一个...

    'select to_number(prop_value,''9999.99'') strucCredit 
     from c_properties
     where prop_name = ''METERING.STRUCT.ONLY.CHARGE'' 
     and id_project = 0'
    

    ...是STABLE:你总是得到相同的结果。不要将它们留在动态 SQL 中,而是创建两个变量并将结果放入其中,然后再执行其他任何操作:

    DECLARE
      ...
      verbSize numeric;
      strucCredit numeric;
    BEGIN
      SELECT to_number(prop_value,'9999.99') INTO verbSize 
      FROM c_properties 
      WHERE prop_name = 'METERING.MAXSIZE.VERBATIM' AND id_pro = 0;
      SELECT to_number(prop_value,'9999.99') INTO strucCredit 
      FROM c_properties 
      WHERE prop_name = 'METERING.STRUCT.ONLY.CHARGE' AND id_pro = 0;
    
      ...
    

    然后在动态 SQL 中使用变量 verbSizestrucCredit。像这样,您只需执行一次这些查询,而不是每次迭代都执行几次

    (2) 所有的动态查询都需要一个GROUP BY 1, 2 子句。

    (3) 子句CASE WHEN coalesce(CAST(v.document_id AS text), '') = '' ... 应该写成CASE WHEN v.document_id IS NULL ...,假设v.document_id 不能是空字符串。

    (4) 您将 RAISE NOTICE 语句更改为 SELECT format(...)。后一种形式不产生任何输出,请改用RAISE NOTICE format(...)

    (5) 重写你的动态 SQL 以使用 format() 函数。

    【讨论】:

    • 函数内是否可以有两个循环返回超过 1 个输出?我能够遵循建议直到#3。 RAISE NOTICE format() 给了我一个错误,我会在这里发布。并且由于某种原因,虽然函数符合要求并且我在中途放置了一个返回脚本来验证脚本,但该脚本看起来不错,但由于某种原因,我猜它没有填充到执行中的变量值中
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-17
    • 2019-03-13
    • 2017-02-10
    • 2011-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多