Skip to Main Content
  • Questions
  • Querying a remote pipelined function

Breadcrumb

Question and Answer

Connor McDonald

Thanks for the question, John.

Asked: February 01, 2013 - 4:12 pm UTC

Last updated: July 23, 2020 - 4:48 am UTC

Version: 10.2.0.5

Viewed 10K+ times! This question is

You Asked

Thanks Tom for your service to the Oracle community.
I usually find solutions to 99% of my questions on your site. But this is among the 1% that I couldn't find any solution anywhere.

I have two database servers DB_A and DB_B.
From DB_A I need to query a pipelined function that resides in remote database DB_B.

I tried this as a simplified example:

On DB_B, I created the pipeline function:
CREATE OR REPLACE TYPE MyObject AS OBJECT(a_day DATE);
/

CREATE OR REPLACE TYPE MyObjType AS TABLE OF MyObject;
/

CREATE OR REPLACE
   FUNCTION MyPipelineFunc( FromDate IN DATE, ToDate IN DATE)
       RETURN MyObjType
       PIPELINED IS

   BEGIN
      FOR Test IN (
          SELECT FromDate + LEVEL Day
             FROM DUAL
             CONNECT BY LEVEL < (ToDate - FromDate)
          )
      LOOP
         PIPE ROW( MyObject(Test.Day));
      END LOOP;

   END MyPipelineFunc;
/

I can invoke this in a query when logged on on DB_B:
   SELECT * FROM TABLE( MyPipelineFunc( SYSDATE, SYSDATE+10));

A_DAY
---------
02-FEB-13
03-FEB-13
04-FEB-13
05-FEB-13
06-FEB-13
07-FEB-13
08-FEB-13
09-FEB-13
10-FEB-13

9 rows selected.

I tried remotely querying this from DB_A:
   SELECT * FROM TABLE( MyPipelineFunc@DB_B( SYSDATE, SYSDATE+10));

But that throws an exception:
   ERROR at line 1:
   ORA-30626: function/procedure parameters of remote object types are not
   supported

So, on DB_B I tried wrapping the pipeline function in a view so I could query something like this:
SELECT * FROM MyPipelineFunc_View@DB_B
   WHERE FromDate = SYSDATE
   AND   ToDate   = SYSDATE+10;

On DB_B I attempted to formulate a view, but I don't know how to pass the predicates Date1 and Date2 as parameters to the pipeline function.
CREATE VIEW MyPipelineFunc_View AS
   SELECT * FROM TABLE( MyPipelineFunc( ???, ???));

(I came up with a kludgy approach that works, but I thought I would ask you first to see if there is an elegant/correct solution to this dilemma before disclosing that.)

Any advice would be welcome.

and Tom said...

there is no clean way to do this because of the object type limitation over a dblink.

what approach did you come up with? I could see how an application context could be useful here.

ops$tkyte%ORA11GR2> create or replace view vv
  2  as
  3  select *
  4    from table( mypipelineFunc(
  5                 to_date( substr( sys_context('userenv', 'client_info'), 1, 8 ),'yyyymmdd' ),
  6                 to_date( substr( sys_context('userenv', 'client_info'), 9, 8 ),'yyyymmdd' ) ) );

View created.

ops$tkyte%ORA11GR2> connect /

ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> select * from table( mypipelineFunc@ora11gr2@loopback( sysdate, sysdate+10 ) );
select * from table( mypipelineFunc@ora11gr2@loopback( sysdate, sysdate+10 ) )
*
ERROR at line 1:
ORA-30626: function/procedure parameters of remote object types are not
supported


ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> exec dbms_application_info.set_client_info@ora11gr2@loopback( to_char(sysdate,'yyyymmdd')|| to_char(sysdate+10,'yyyymmdd') );

PL/SQL procedure successfully completed.

ops$tkyte%ORA11GR2> select * from vv@ora11gr2@loopback;

A_DAY
---------
07-FEB-13
08-FEB-13
09-FEB-13
10-FEB-13
11-FEB-13
12-FEB-13
13-FEB-13
14-FEB-13
15-FEB-13

9 rows selected.



you can use client info (if the data is small enough like this was) or a real application context to push parameters into the view.

Rating

  (6 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

My solution

John Gasch, February 07, 2013 - 4:13 pm UTC

Thanks Tom. Your approach of using sys_context to pass parameters worked fine. I'm sharing my 'kludgy approach', but in retrospect it may be no more kludgy than yours. Each achieve my objective. Rather than pass parameters in an application context, I elected to push them to the remote db via a global temp table. The working example is shown below. (I repeat a portion of the above post in the interest of completeness of the script.)

Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.5.0 - Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL> -- Connect to remote DB to create pipeline function
SQL> --  and supporting objects for test.
SQL> CONNECT /@DB_B
Connected.

SQL> CREATE OR REPLACE TYPE MyObject AS OBJECT(a_day DATE);
  2  /

Type created.

SQL> CREATE OR REPLACE TYPE MyObjType AS TABLE OF MyObject;
  2  /

Type created.

SQL> CREATE OR REPLACE
  2     FUNCTION MyPipelineFunc( FromDate IN DATE, ToDate IN DATE)
  3         RETURN MyObjType
  4         PIPELINED IS
  5
  6     BEGIN
  7        FOR Test IN (
  8            SELECT FromDate + LEVEL Day
  9               FROM DUAL
 10               CONNECT BY LEVEL < (ToDate - FromDate)
 11            )
 12        LOOP
 13           PIPE ROW( MyObject(Test.Day));
 14        END LOOP;
 15
 16     END MyPipelineFunc;
 17  /

Function created.

SQL> -- I can invoke this in a query when logged on to remote database DB_B:

SQL> SELECT * FROM TABLE( MyPipelineFunc( SYSDATE, SYSDATE+10));

A_DAY
---------
08-FEB-13
09-FEB-13
10-FEB-13
11-FEB-13
12-FEB-13
13-FEB-13
14-FEB-13
15-FEB-13
16-FEB-13

9 rows selected.

SQL> -- Remotely querying this from DB_A throws an exception:
SQL> CONNECT /@DB_A
Connected.

SQL>    SELECT * FROM TABLE( MyPipelineFunc@LANDSAT5( SYSDATE, SYSDATE+10));
   SELECT * FROM TABLE( MyPipelineFunc@LANDSAT5( SYSDATE, SYSDATE+10))
*
ERROR at line 1:
ORA-30626: function/procedure parameters of remote object types are not
supported


SQL> -- Here is Tom's solution in my environment using sys_context
SQL> CONNECT /@DB_B
Connected.

SQL> create or replace view vv
  2    as
  3    select *
  4      from table( mypipelineFunc(
  5                   to_date( substr( sys_context('userenv', 'client_info'), 1, 8 ),'yyyymmdd' ),
  6                   to_date( substr( sys_context('userenv', 'client_info'), 9, 8 ),'yyyymmdd' ) ) );

View created.

SQL> CONNECT /@DB_A
Connected.

SQL> exec dbms_application_info.set_client_info@DB_B( to_char(sysdate,'yyyymmdd')|| to_char(sysdate+10, 'yyyymmdd') );

PL/SQL procedure successfully completed.

SQL> select * from vv@Landsat5;

A_DAY
---------
08-FEB-13
09-FEB-13
10-FEB-13
11-FEB-13
12-FEB-13
13-FEB-13
14-FEB-13
15-FEB-13
16-FEB-13

9 rows selected.

SQL> -- OK - That works fine!

SQL> --------- Here is my solution using GTT -----------
SQL>
SQL> CONNECT /@DB_B
Connected.

SQL> -- I create a temp table on the remote database to serve as a
SQL> --  placeholder for the pipeline function parameters.

SQL> CREATE GLOBAL TEMPORARY
  2     TABLE MyPipeFunc_Parameters (
  3           FromDate DATE,
  4           ToDate DATE
  5     ) ON COMMIT PRESERVE ROWS;

Table created.

SQL> -- And now the magic. I found that I can query the remote pipeline function
SQL> --  in a view by passing its parameters thru a joined table. But the parameter
SQL> --  table must precede the pipeline function in the join list (due to scoping
SQL> --  rules???). I am curious whether I may be exploiting an unintended "feature"
SQL> --  that some day might be "fixed", thus breaking this approach.

SQL> CREATE OR REPLACE VIEW vv
  2    AS
  3    SELECT A_Day
  4      FROM MyPipeFunc_Parameters param,
  5           TABLE( MyPipeLinefunc( param.FromDate, param.ToDate));

View created.

SQL> -- Now, return to the local database to test the new remote view.
SQL> CONNECT /@DB_A
Connected.

SQL> -- Load the parameters into a record in the temp parameter table.
SQL> --  This assumes that one and only one record is loaded into the GTT param table,
SQL> --  So I first empty it for good measure.

SQL> DELETE FROM MyPipeFunc_Parameters@DB_B;

0 rows deleted.

SQL> INSERT INTO MyPipeFunc_Parameters@DB_B VALUES (SYSDATE, SYSDATE+10);

1 row created.

SQL> -- Then I can remotely query the pipeline function via the remote view.
SQL>
SQL> SELECT * FROM vv@DB_B;

A_DAY
---------
08-FEB-13
09-FEB-13
10-FEB-13
11-FEB-13
12-FEB-13
13-FEB-13
14-FEB-13
15-FEB-13
16-FEB-13

9 rows selected.

SQL> -- I'm happy with that. It works and accomplishes the objective.i
SQL> -- Now I can query that remote view in a local SQL statement.

Tom Kyte
February 11, 2013 - 9:01 am UTC


SQL> -- And now the magic. I found that I can query the remote pipeline
function
SQL> -- in a view by passing its parameters thru a joined table. But the
parameter
SQL> -- table must precede the pipeline function in the join list (due to
scoping
SQL> -- rules???). I am curious whether I may be exploiting an unintended
"feature"
SQL> -- that some day might be "fixed", thus breaking this approach.


you are not exploiting an unintended side effect, but rather you are doing "table unnesting"


http://docs.oracle.com/cd/E11882_01/appdev.112/e11822/adobjdes.htm#ADOBJ7446

Vincent Malgrat, February 12, 2013 - 9:06 am UTC

Hi Tom,

regarding the above poster's workaround: it works fine but the documentation about temporary tables states: Distributed transactions are not supported for temporary tables.
http://docs.oracle.com/cd/E11882_01/server.112/e26088/statements_7002.htm#SQLRF54447

I've always been intrigued by this sentence, does it apply in this case, when can we use remote temporary tables?

Regards,
Tom Kyte
February 12, 2013 - 9:37 am UTC

It will actually work in this case - they have issues with XA transactions and multiple database links/session being in use at the database having the global temporary table (like a loopback link might cause)

the dblink issues a sql use two users in the remote DB

wildck, February 25, 2013 - 9:11 am UTC

the dblink use of doubt, the same SQL with a remote DB with
Following practice database influential not of DB1 under two user permissions is not merger, there is no reason, so I'm going to do so now, do not know will give two DB bring bad impact, such as abnormal DB1 in the Session


- Company Database1 DB1,

Establish UserA and userB, DB under user-A select table1 permissions, user-b select Table2 permissions

The department Databse2 referred DB2

- DB DB1 is created using the user-a dblink, named db1-usera
- DB with the user-b the DB1 the dblink, named db1-userb

Then written in DB2 sql run, DB1 select data from the remote, table1 and table2 user is a different

select row 1, column 2, column 3 from table@db1-usera union all select row 1, column 2, column 3 from table2@db1-userb

Pipelined function or View

Girish, March 07, 2013 - 4:47 am UTC

Hi Tom,

If a query is used to retrieve data.Which one is the best option to implement using a view or a pipelined function from performance point of view

We can't use single query as it is because query is very big( 2 pages ) and many applications will be using result of this query

Thanks,
Girish
Tom Kyte
March 07, 2013 - 8:59 am UTC

view, if you do not need to procedurally process the data, use a view.

further query on suggested approach ..

PS, July 22, 2020 - 7:13 am UTC

Subject: Accessing Pipelined table function over db link is not working in Apex

Dear All,

Sorry for posting on old thread but this is most relevant post for my query.

On remote DB, below activities done:

1.
CREATE OR REPLACE CONTEXT apex_sc_ctx USING schema_a.pkg_4_context_mgmt ACCESSED GLOBALLY;


2. Created schema_a.pkg_4_context_mgmt

This package has below stored programs:

Procedure: set_context_values : Takes parameters as input and set up application context like ..

DBMS_SESSION.SET_CONTEXT('apex_sc_ctx', 'lc_machine', p_machine);

Function: GET_DATA_FNC

Get parameters and returns SQL_REFCURSOR.

Function: GET_DATA_PIPE_FNC

which utilizes object type like TYPE DATA_TAB_TY IS TABLE OF sometable%ROWTYPE; Takes input SQL_REFCURSOR from GET_DATA_FNC & form pipelined output.

3. Created view over above pipelined function:

CREATE OR REPLACE VIEW V_DATA
AS
SELECT *
  FROM TABLE(schema_a.pkg_4_context_mgmt .GET_DATA_PIPE_FNC (pkg_4_context_mgmt .GET_DATA_FNC(sys_context('apex_scheduler_ctx','lc_machine') ) ) );



At local database, I have a public db link connecting to schema_a of remote database.

1. I fire schema_a.pkg_4_context_mgmt .set_context_values @db_lnk(:P7_MACHINE); to set the application context. There are many parameters, hence separate application context was created.

2. When I run above view [V_DATA] in sql developer. It takes in to account the parameter passed and provide filtered output. Eg. SELECT * FROM V_DATA@db_lnk;

But when I run the same sql statement in SQL Workshop => SQL Command, it doesn't take in to account the application context set up on remote db and entire dataset is returned.

I also created a classic report based on above view, still the outcome remains same.

Versions:

Remote DB: Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production

Local DB : Oracle Database 18c Express Edition Release 18.0.0.0.0 - Production Version 18.4.0.0.0

Oracle Apex Build: 20.1.0.00.13

Can you please suggest if I am missing anything?
Connor McDonald
July 23, 2020 - 4:48 am UTC

The problem with SQL Command is that every request you make gets directed to a session in the connection pool. There is no guarantee that you are using the same session each time.

Try saving the commands as a script and testing that, ie

- set context
- select * from v_data

and check if that works.

I'm not sure though if this will solve the issue when it comes to using this in a Classic Report. If not, you might need to do something like

- use PLSQL to set the context, query the V_DATA and save in a local table keyed by apex session ID
- Classic Report off the local table

further query on suggested approach ..

PS, July 23, 2020 - 10:37 am UTC

Thank you so much Connor for your time & reply.

What I failed to understand is ..

Through Oracle Apex, "After Submit" I am firing the procedure which sets application context on remote DB.

Once I do it, If I query this view from any PL/SQL Developer session [either local i.e. schema_a or remote i.e. APEX_PUBLIC_USER ] result would be filtered according to the application context.
Certainly, here we must be creating new sessions .. but still it works.

But it's only with Oracle Apex that application context is not getting considered. Ultimate aim is to create a report based on the view.

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library