Oracle:从sys_refcursor返回动态结果集

时间:2016-03-20 18:15:25

标签: oracle plsql sys-refcursor pipelined-function

我需要一个可以从sys_refcursor返回表的流水线函数,并且sys_refcursor参数在编译时是未知的。

例如:

select * 
from  table(pipeline_func(cursor(select 1 col_1, 2 col_2 from dua)))

select * 
from  table(pipeline_func(cursor(select 1 col_1, 2 col_2, 3 col_3 from dua))).

我已经阅读了这篇文章 return resultset from function

并尝试修改代码,以便它可以接受sys_refcursor作为参数而不是字符串SQL语句。但错误发生在这一行:

pkg_dynamic_pipeline.r_sql.execute := dbms_sql.execute(pkg_dynamic_pipeline.r_sql.cursor); 

错误按摩:光标无效。

你能帮我修一下这个错误还是给我提示解决我的问题?

关注我的代码:

CREATE OR REPLACE type dynamic_pipeline as object
(
     atype anytype,
     static function ODCITableDescribe
     (
        rtype out anytype, 
        sql_cur  in out SYS_REFCURSOR
     )
     return number,

     static function ODCITablePrepare
     (
        sctx      out dynamic_pipeline,
        tf_info   in  sys.ODCITabfuncinfo,
        sql_cur  in out SYS_REFCURSOR
      )
      return number,

     static function ODCITableStart
     (
        sctx  in out dynamic_pipeline,
        sql_cur  in out SYS_REFCURSOR
     )
     return number,

     member function ODCITablefetch
     ( 
        self  in out dynamic_pipeline,
        nrows in     number,
        rws   out    anydataset
     )
     return number,

     member function ODCITableClose
     (
        self in dynamic_pipeline
     )
     return number
 );


CREATE OR REPLACE package ICMDB_OAA.pkg_dynamic_pipeline as
 /*
  * Global Types
  */
  -- Describe array.
  type dynamic_sql_rec is record
  (
        cursor    integer,
        column_cnt  pls_integer,
        description dbms_sql.desc_tab2,
        execute     integer
   );


   type anytype_metadata_rec is record
   (
        precision pls_integer,
        scale     pls_integer,
        length    pls_integer,
        csid      pls_integer,
        csfrm     pls_integer,
        schema    varchar2(30),
        type      anytype,
        name      varchar2(30),
        version   varchar2(30),
        attr_cnt  pls_integer,
        attr_type anytype,
        attr_name varchar2(128),
        typecode  pls_integer
    );

    /*
    * Global Variables
    */
    -- SQL descriptor.
     r_sql dynamic_sql_rec;

     /*
      * function will run the given SQL
      */
     /*
        function querydb(p_stmt in varchar2)
        return anydataset pipelined using dyn_pipeline;
     */

     function querydb(p_cursor in sys_refcursor)
     return anydataset pipelined using dynamic_pipeline;

end pkg_dynamic_pipeline;
/

CREATE OR REPLACE type body dynamic_pipeline
as
    /*
     * DESC step. this will be called at hard parse and will create
     * a physical type in the DB Schema based on the select columns.
     */
    static function ODCITableDescribe
    (
        rtype out anytype, 
        sql_cur  in out  SYS_REFCURSOR
    )
    return number
    is

      /* Variables */
      -- Type to hold the dbms_sql info (description)
      r_sql   pkg_dynamic_pipeline.dynamic_sql_rec;
       -- Type to create (has all the columns) of the sql query.
       t_anyt  anytype;
       -- SQL query that will be made up from the 2 passed in queries.
       -- v_sql   varchar2(32767);
        -- cur_local sys_refcursor;
     begin
            DBMS_OUTPUT.PUT_LINE('ODCITableDescribe');    

      /*
       * Parse the SQL and describe its format and structure.
       */
      -- v_sql := replace(stmt, ';', null);

      -- open, parse and discover all info about this SQL.
      -- r_sql.cursor := dbms_sql.open_cursor;
      -- dbms_sql.parse( r_sql.cursor, v_sql, dbms_sql.native );
      r_sql.cursor := DBMS_SQL.TO_CURSOR_NUMBER(sql_cur);
      dbms_sql.describe_columns2( r_sql.cursor, r_sql.column_cnt, r_sql.description );
      --dbms_sql.close_cursor( r_sql.cursor );

      -- Start to create the physical type.
      anytype.BeginCreate( DBMS_TYPES.TYPECODE_OBJECT, t_anyt );

      -- Loop through each attribute and add to the type.
      for i in 1 .. r_sql.column_cnt
      loop

        t_anyt.AddAttr(r_sql.description(i).col_name,
                       case
                         when r_sql.description(i).col_type in (1,96,11,208)
                         then dbms_types.typecode_varchar2
                        when r_sql.description(i).col_type = 2
                         then dbms_types.typecode_number
                         when r_sql.description(i).col_type in (8,112)
                         then dbms_types.typecode_clob
                         when r_sql.description(i).col_type = 12
                         then dbms_types.typecode_date
                         when r_sql.description(i).col_type = 23
                         then dbms_types.typecode_raw
                         when r_sql.description(i).col_type = 180
                         then dbms_types.typecode_timestamp
                         when r_sql.description(i).col_type = 181
                         then dbms_types.typecode_timestamp_tz
                         when r_sql.description(i).col_type = 182
                         then dbms_types.typecode_interval_ym
                         when r_sql.description(i).col_type = 183
                         then dbms_types.typecode_interval_ds
                         when r_sql.description(i).col_type = 231
                         then dbms_types.typecode_timestamp_ltz
                       end,
                       r_sql.description(i).col_precision,
                       r_sql.description(i).col_scale,
                       r_sql.description(i).col_max_len,
                       r_sql.description(i).col_charsetid,
                       r_sql.description(i).col_charsetform );
      end loop;

      t_anyt.EndCreate;

      -- set the output type to our built type.
      ANYTYPE.BeginCreate(dbms_types.TYPECODE_TABLE, rtype);
      rtype.SetInfo(null, null, null, null, null, t_anyt,
                    dbms_types.TYPECODE_OBJECT, 0);
      rtype.EndCreate();

      sql_cur:= DBMS_SQL.TO_REFCURSOR(r_sql.cursor);

      DBMS_OUTPUT.PUT_LINE('ODCITableDescribe End');  

      return ODCIConst.Success;

    end ODCITableDescribe;


    /*
     * PREPARE step. Initialise our type.
     */
   static function ODCITablePrepare
   (
        sctx      out dynamic_pipeline,
        tf_info   in  sys.ODCITabfuncinfo,
        sql_cur  in  out SYS_REFCURSOR
   )
       return number
     is

       /* Variables */
       -- Meta data.
       r_meta   pkg_dynamic_pipeline.anytype_metadata_rec;

     begin

       DBMS_OUTPUT.PUT_LINE('ODCITablePrepare');

       r_meta.typecode := tf_info.rettype.getattreleminfo(
                           1, r_meta.precision, r_meta.scale, r_meta.length,
                           r_meta.csid, r_meta.csfrm, r_meta.type, r_meta.name
                         );

      sctx := dynamic_pipeline(r_meta.type);

      DBMS_OUTPUT.PUT_LINE('ODCITablePrepare End');

      return odciconst.success;

    end;


    /*
    * START step. this is where we execute the cursor prior to fetching from it.
     */
   static function ODCITableStart
   (
        sctx  in out dynamic_pipeline,
        sql_cur  in out SYS_REFCURSOR
   )
      return number
    is

      /* Variables */
      r_meta pkg_dynamic_pipeline.anytype_metadata_rec;
      --v_sql varchar2(32767);
      --cur_local sys_refcursor;
    begin

      DBMS_OUTPUT.PUT_LINE('ODCITableStart');

      --cur_local:=sql_cur;

      --v_sql := replace(stmt, ';', null);
      --pkg_dynamic_pipeline.r_sql.cursor := dbms_sql.open_cursor;
      pkg_dynamic_pipeline.r_sql.cursor := DBMS_SQL.TO_CURSOR_NUMBER(sql_cur);
      --dbms_sql.parse(pkg_dynamic_pipeline.r_sql.cursor, v_sql, dbms_sql.native);
      dbms_sql.describe_columns2(pkg_dynamic_pipeline.r_sql.cursor,
                                 pkg_dynamic_pipeline.r_sql.column_cnt,
                                 pkg_dynamic_pipeline.r_sql.description);

      -- define all the columns found to let Oracle know the datatypes.
      DBMS_OUTPUT.PUT_LINE('ODCITableStart. column_cnt['||pkg_dynamic_pipeline.r_sql.column_cnt||']');
      for i in 1..pkg_dynamic_pipeline.r_sql.column_cnt
      loop

        r_meta.typecode := sctx.atype.GetAttrElemInfo(
                             i, r_meta.precision, r_meta.scale, r_meta.length,
                             r_meta.csid, r_meta.csfrm, r_meta.type, r_meta.name
                           );

        case r_meta.typecode
          when dbms_types.typecode_varchar2
          then
            dbms_sql.define_column(pkg_dynamic_pipeline.r_sql.cursor, i, '', 32767);
          when dbms_types.typecode_number
          then
            dbms_sql.define_column(pkg_dynamic_pipeline.r_sql.cursor, i, cast(null as number));
          when dbms_types.typecode_date
          then
            dbms_sql.define_column(pkg_dynamic_pipeline.r_sql.cursor, i, cast(null as date));
          when dbms_types.typecode_raw
          then
            dbms_sql.define_column_raw(pkg_dynamic_pipeline.r_sql.cursor, i, cast(null as raw), r_meta.length);
          when dbms_types.typecode_timestamp
          then
            dbms_sql.define_column(pkg_dynamic_pipeline.r_sql.cursor, i, cast(null as timestamp));
          when dbms_types.typecode_timestamp_tz
          then
            dbms_sql.define_column(pkg_dynamic_pipeline.r_sql.cursor, i, cast(null as timestamp with time zone));
          when dbms_types.typecode_timestamp_ltz
          then
            dbms_sql.define_column(pkg_dynamic_pipeline.r_sql.cursor, i, cast(null as timestamp with local time zone));
          when dbms_types.typecode_interval_ym
          then
            dbms_sql.define_column(pkg_dynamic_pipeline.r_sql.cursor, i, cast(null as interval year to month));
          when dbms_types.typecode_interval_ds
          then
            dbms_sql.define_column(pkg_dynamic_pipeline.r_sql.cursor, i, cast(null as interval day to second));
          when dbms_types.typecode_clob
          then
            case pkg_dynamic_pipeline.r_sql.description(i).col_type
              when 8
              then
                dbms_sql.define_column_long(pkg_dynamic_pipeline.r_sql.cursor, i);
              else
                dbms_sql.define_column(pkg_dynamic_pipeline.r_sql.cursor, i, cast(null as clob));
            end case;
        end case;
      end loop;

      -- execute the SQL.
      pkg_dynamic_pipeline.r_sql.execute := dbms_sql.execute(pkg_dynamic_pipeline.r_sql.cursor);

      sql_cur:= DBMS_SQL.TO_REFCURSOR(pkg_dynamic_pipeline.r_sql.cursor);

      DBMS_OUTPUT.PUT_LINE('ODCITableStart End');

      return odciconst.success;

    end ODCITablestart;

    /*
     * FETCH step.
     */
    member function ODCITablefetch
    (
            self   in out dynamic_pipeline,
            nrows  in     number,
            rws    out    anydataset
     )
     return number
    is
      /* Variables */
      -- Buffers to hold values.
      v_vc_col       varchar2(32767);
      v_num_col      number;
      v_date_col     date;
      v_raw_col      raw(32767);
      v_raw_error    number;
      v_raw_len      integer;
      v_int_ds_col   interval day to second;
      v_int_ym_col   interval year to month;
      v_ts_col       timestamp;
      v_tstz_col     timestamp with time zone;
      v_tsltz_col    timestamp with local time zone;
      v_clob_col     clob;
      v_clob_offset  integer := 0;
      v_clob_len     integer;
      -- Metadata
      r_meta  pkg_dynamic_pipeline.anytype_metadata_rec;

    begin

        DBMS_OUTPUT.PUT_LINE('ODCITablefetch');

      if dbms_sql.fetch_rows( pkg_dynamic_pipeline.r_sql.cursor ) > 0
      then

        -- Describe to get number and types of columns.
        r_meta.typecode := self.atype.getinfo(
                             r_meta.precision, r_meta.scale, r_meta.length,
                             r_meta.csid, r_meta.csfrm, r_meta.schema,
                             r_meta.name, r_meta.version, r_meta.attr_cnt
                           );

        anydataset.begincreate(dbms_types.typecode_object, self.atype, rws);
        rws.addinstance();
        rws.piecewise();

        -- loop through each column extracting value.
        for i in 1..pkg_dynamic_pipeline.r_sql.column_cnt
        loop

          r_meta.typecode := self.atype.getattreleminfo(
                               i, r_meta.precision, r_meta.scale, r_meta.length,
                               r_meta.csid, r_meta.csfrm, r_meta.attr_type,
                               r_meta.attr_name
                             );

          case r_meta.typecode
            when dbms_types.typecode_varchar2
            then
              dbms_sql.column_value(pkg_dynamic_pipeline.r_sql.cursor, i, v_vc_col);
              rws.setvarchar2(v_vc_col);
            when dbms_types.typecode_number
            then
              dbms_sql.column_value(pkg_dynamic_pipeline.r_sql.cursor, i, v_num_col);
              rws.setnumber(v_num_col);
            when dbms_types.typecode_date
            then
              dbms_sql.column_value(pkg_dynamic_pipeline.r_sql.cursor, i, v_date_col);
              rws.setdate(v_date_col);
            when dbms_types.typecode_raw
            then
              dbms_sql.column_value_raw(pkg_dynamic_pipeline.r_sql.cursor, i, v_raw_col,
                 v_raw_error, v_raw_len);
              rws.setraw(v_raw_col);
            when dbms_types.typecode_interval_ds
            then
              dbms_sql.column_value(pkg_dynamic_pipeline.r_sql.cursor, i, v_int_ds_col);
              rws.setintervalds(v_int_ds_col);
            when dbms_types.typecode_interval_ym
            then
              dbms_sql.column_value(pkg_dynamic_pipeline.r_sql.cursor, i, v_int_ym_col);
              rws.setintervalym(v_int_ym_col);
            when dbms_types.typecode_timestamp
            then
              dbms_sql.column_value(pkg_dynamic_pipeline.r_sql.cursor, i, v_ts_col);
              rws.settimestamp(v_ts_col);
            when dbms_types.typecode_timestamp_tz
            then
              dbms_sql.column_value(pkg_dynamic_pipeline.r_sql.cursor, i, v_tstz_col);
              rws.settimestamptz(v_tstz_col);
           when dbms_types.typecode_timestamp_ltz
           then
              dbms_sql.column_value(pkg_dynamic_pipeline.r_sql.cursor, i, v_tsltz_col);
              rws.settimestampltz(v_tsltz_col);
           when dbms_types.typecode_clob
           then
             case pkg_dynamic_pipeline.r_sql.description(i).col_type
               when 8
               then
                 loop
                   dbms_sql.column_value_long(pkg_dynamic_pipeline.r_sql.cursor, i, 32767, v_clob_offset,
                                              v_vc_col, v_clob_len);
                   v_clob_col := v_clob_col || v_vc_col;
                   v_clob_offset := v_clob_offset + 32767;
                   exit when v_clob_len < 32767;
                 end loop;
               else
                 dbms_sql.column_value(pkg_dynamic_pipeline.r_sql.cursor, i, v_clob_col);
             end case;
             rws.setclob(v_clob_col);
          end case;
        end loop;

        rws.endcreate();

      end if;

      DBMS_OUTPUT.PUT_LINE('ODCITablefetch End');

      return ODCIConst.Success;

    end;

    /*
     * CLOSE step. close the cursor.
     */
    member function ODCITableClose(self in dynamic_pipeline)
      return number
    is
    begin
      DBMS_OUTPUT.PUT_LINE('ODCITableClose');
      dbms_sql.close_cursor( pkg_dynamic_pipeline.r_sql.cursor );
      pkg_dynamic_pipeline.r_sql := null;
      DBMS_OUTPUT.PUT_LINE('ODCITableClose End');
      return odciconst.success;
    end ODCITableClose;

  end;

/

1 个答案:

答案 0 :(得分:0)

注释掉DYNAMIC_PIPELINE类型主体中的第206和208行,它将起作用:

--pkg_dynamic_pipeline.r_sql.execute := dbms_sql.execute(pkg_dynamic_pipeline.r_sql.cursor);

--sql_cur:= DBMS_SQL.TO_REFCURSOR(pkg_dynamic_pipeline.r_sql.cursor);

例如:

SQL> select * from table(pkg_dynamic_pipeline.querydb(cursor(select 1 col_1, 2 col_2 from dual)));

     COL_1      COL_2
---------- ----------
         1          2

在测试修复程序之前,运行下面的块来清除包变量并强制进行硬解析:

begin
    dbms_session.reset_package;
    execute immediate 'alter system flush shared_pool';
end;
/

您可能希望将代码基于我的Method4 open source program而不是其他Stackoverflow答案。两个版本都基于Adrian Billington的原始版本,但我添加了许多有用的类型修复。例如,原始版本不适用于NVARCHAR2,float,30字节列名等。

另外,如果有充分的理由使用游标而不是字符串,我愿意为我的项目添加游标版本,并且您认为它可能对其他人有帮助。