Skip to Main Content

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, Vasily.

Asked: May 22, 2019 - 1:31 pm UTC

Last updated: June 11, 2019 - 10:07 am UTC

Version: 12.1

Viewed 1000+ times

You Asked

Hello TOM :)

I try to make anytype in java stored procedure and use it to create anydataset in PL/SQL. But I get error ORA-22625. Why this error occures and how can I fix it? I do it according to guides, for example https://docs.oracle.com/database/121/JJDBC/oraint.htm#JJDBC28154 , Examples 4-2 and 4-3.
Script https://livesql.oracle.com/apex/livesql/s/ie97h511l4nyenahzz0kxb3lp reproduces error on Oracle Database 12.1 but looks like LiveSQL do not support java so error there not real.
This anytype created in java looks fine, I checked it's structure in PL/SQL.
If I create anytype in PL/SQL creating anydataset with it works fine.

Thanks for your great work over these years TOM! :)



with LiveSQL Test Case:

and Chris said...

I can reproduce the problem. I'm not sure why you're getting this though; ANYTYPE/ANYDATASET isn't exactly my forte ;)

I'm asking around internally.

That said, given this works if you use pure PL/SQL - why are you using a Java stored procedure?

Pasting the PL/SQL in the Java back into PL/SQL and there's no error for me:

DECLARE 
  atyp anytype; 
  outset anydataset ; 
BEGIN 
 
  begin
    anytype.begincreate( dbms_types.typecode_object, atyp );
    atyp.addattr
    ( 'f1'
    , dbms_types.typecode_number
    , null
    , null
    , null
    , null
    , null
    ) ;
    atyp.addattr
    ( 'f2'
    , dbms_types.typecode_varchar2
    , NULL
    , NULL
    , 100
    , null
    , null
    ) ;
    atyp.endcreate;
  end;
  
  anydataset.begincreate( dbms_types.typecode_object, atyp, outset ); 
  for i in 1 .. 5 
  loop 
    outset.addinstance; 
    outset.piecewise(); 
    outset.setnumber( i ); 
    outset.setvarchar2( 'row: ' || to_char( i ) ); 

  end loop; 
  outset.endcreate; 

  dbms_output.put_line ( 'Finished' );
END ; 
/

Finished


Rating

  (6 ratings)

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

Comments

why are you using a Java stored procedure?

Vasily Suvorov, June 06, 2019 - 8:49 am UTC

Hi Chris!

Final goal is to reach outer world using JDBC and use produced resultset in SQL as virtual table. For this we need pipelined table function with interface approach as described in guide here:
https://docs.oracle.com/database/121/ADDCI/pipe_paral_tbl.htm#ADDCI4708
and here https://docs.oracle.com/database/121/ADDCI/pipe_paral_tbl_ref.htm#ADDCI4230
Example of Java implementation of ODCI shown here in guide:
https://docs.oracle.com/database/121/ADDCI/pipelined_example.htm#ADDCI4932
Function ODCITableFetch takes anydataset parameter in case of unknon resultset structure so we need to construct it in Java and pass to PL/SQL.
Chris Saxon
June 06, 2019 - 10:06 am UTC

I'm still confused.

Given that you have PL/SQL calling Java calling PL/SQL, why precisely do you need to use Java in-between? What exactly is stopping you doing the whole thing in PL/SQL?

Surely you can build your pipelined table function in PL/SQL, and have JDBC clients read that?

What exactly is stopping you doing the whole thing in PL/SQL

Vasily suvorov, June 06, 2019 - 10:35 am UTC

I need to connect to other databases, send any selects there and get resultset. Without creating dblinks, databases must be completly independent. So i connect to another database using jdbc and get resultset. Then i need to pass this resultset to sql engine and use as table in queries.
I create anydataset in pl/sql because according to guides java has no means of creating anydataset. It can only be created in pl/sql or c. Or may be it already implemented in some later version?
Chris Saxon
June 06, 2019 - 2:31 pm UTC

Hmmm, this sounds a bit fishy to me...

If you're doing this using Java Stored Procedures, I'm not sure that counts as having completely independent databases!

In any case: why do you need anytype/anydataset? Why can't you use a ref cursor?

Why can't you use a ref cursor

Vasily Suvorov, June 06, 2019 - 4:20 pm UTC

I mean database itself, without bussiness logic, technical stuff.

I think about making both versions, one with java implementation of ODCI interface and another with ref cursor passing from java to functions of pl/sql ODCI interface. Then i can compare which works faster :) Do you have suggestions which approach is "better" ? :)
Chris Saxon
June 06, 2019 - 4:46 pm UTC

Ref cursors are widely used, well understood, and easy to work with.

I'm not sure the any* types meet any of those criteria ;)

So I'd stick with ref cursors.

not sure the any* types meet any of those criteria

Vasily Suvorov, June 06, 2019 - 5:09 pm UTC

But we can't select from cursor. We still need to pass data from cursor to ODCI interface and for data with unknown structure we need anydataset and anytype. Approach with passing ref cursor from java to pl/sql just shifts parsing cursor and making anydataset from java to pl/sql, not removing evil any* types :)
Chris Saxon
June 07, 2019 - 8:08 am UTC

I don't understand what you're trying to do here.

It sounds like you're trying to build a generic "accept any query, return any result set" process. Which is generally a bad idea.

But I'm still not seeing why you need the any* types. You can have the Java return to a sys_refcursor:

create or replace and resolve java source named refcur as
import java.sql.*;
import java.io.*;
import oracle.jdbc.*;

/*
 *  Mapping REF CURSOR to java.sql.Resultset
 */

public class refcur
{
  /*
   * Procedure returning a REF CURSOR via OUT parameter
   */
  public static void refcurproc (ResultSet rs[])
    throws SQLException
  {
    Connection conn = null;  
    conn = DriverManager.getConnection("jdbc:oracle:kprb:");
    ((OracleConnection)conn).setCreateStatementAsRefCursor(true);
    Statement stmt = conn.createStatement();
    ((OracleStatement)stmt).setRowPrefetch(1);
    ResultSet rset = stmt.executeQuery("select * from EMP order by empno");
    rs[0] = rset;
    // fetch one row
    if (rset.next())
    {
      System.out.println("Ename = " + rset.getString(2));
    }
   }

  /*
   * Function returning a REF CURSOR 
   */
  public static ResultSet refcurfunc ()
    throws SQLException
  {
    Connection conn = null;  
    conn = DriverManager.getConnection("jdbc:oracle:kprb:");
    ((OracleConnection)conn).setCreateStatementAsRefCursor(true);
    Statement stmt = conn.createStatement();
    ((OracleStatement)stmt).setRowPrefetch(1);
    ResultSet rset = stmt.executeQuery("select * from EMP order by empno");
    // fetch one row
    if (rset.next())
    {
      System.out.println("Ename = " + rset.getString(2));
    }
    return rset;
   }
}
/
show errors;


create or replace package refcur_pkg as
type EmpCurTyp IS REF CURSOR;
  function rcfunc return sys_refcursor;
  procedure rcproc (rc OUT sys_refcursor);
end refcur_pkg;
/
show errors;

create or replace package body refcur_pkg as 
 
procedure rcproc(rc OUT sys_refcursor)
 as language java 
 name 'refcur.refcurproc(java.sql.ResultSet[])';
 
function rcfunc return sys_refcursor
 as language java 
 name 'refcur.refcurfunc() returns java.sql.ResultSet';

end refcur_pkg;
/
show errors;

---
--- A demo PL/SQL for calling the Java procedure/function
--- then processing the refcursor. 
--- A Java app would retrieve the result set from the Ref Cursor
---

set serveroutput on
call dbms_java.set_output(50000);

declare
  employee emp%ROWTYPE;
  rc sys_refcursor;
begin
  dbms_output.put_line(' ** Calling REF CURSOR PROCEDURE' );
  refcur_pkg.rcproc(rc);
---
--- Alternatively the refcurfunc could be called as follows 
--- rc = refcur_pkg.rcfunc();
---
  LOOP
   fetch rc into employee;
   exit when rc%notfound;
   dbms_output.put_line(' Name = ' || employee.ENAME ||
            ' Department = ' || employee.DEPTNO);
  end loop;
close rc;
end;
/
show errors;

** Calling REF CURSOR PROCEDURE
Ename = SMITH
 Name = ALLEN Department = 30
 Name = WARD Department = 30
 Name = JONES Department = 20
 Name = MARTIN Department = 30
 Name = BLAKE Department = 30
 Name = CLARK Department = 10
 Name = SCOTT Department = 20
 Name = KING Department = 10
 Name = TURNER Department = 30
 Name = ADAMS Department = 20
 Name = JAMES Department = 30
 Name = FORD Department = 20
 Name = MILLER Department = 10


OK, so the PL/SQL client needs to know the shape of the result set.

But my understanding is you have DB1 send a query to DB2 via Java. Then get the results back. So surely DB1 has some idea what it's asking for? Otherwise how can it construct the query?

accept any query, return any result set

Vasily Suvorov, June 07, 2019 - 1:52 pm UTC

Yes I try to make "accept any query, return any result set" pipelined table function to use it like
select * from table ( make_any_query_function ( "database_alias" , "select * from emp" ) )
Generic approaches like this work for us very well for many years so i am going to try and see what happens :)
There is example in Live SQL: https://livesql.oracle.com/apex/livesql/s/fc9a3nur5hwda9vj95gv1qdbd
But in Oracle 12 it works only in SQL, in PL/SQL there is error ORA-22905. In Oracle 18 it works fine.

We can return resultset from java function to PL/SQL but how we will pass resultset from remote database to to this returning dataset? They are from different connections.

"my understanding is you have DB1 send a query to DB2 via Java" - exactly. Yes database which sends query knows what it wants to get, we write sql text for this like we write it for db_links.

Have you looked into installing ORDS and using REST enabled SQL instead?

Vasily Suvorov, June 11, 2019 - 9:40 am UTC

No I did not think about using REST Enabled SQL. Looks like That Jeff Smith try to fix it spamming us with RESTful Services in his mailing list :) We use Web Services sometimes and I try to make more efficient mechanism. With XML and JSON we have to convert binary data types to text and then back to binary. With JDBC we can get Oracle data types in their binary form and just copy them around without conversions (in cases where both batabases are Oracle). This looks more suitable for large datasets.
Chris Saxon
June 11, 2019 - 10:07 am UTC

Obviously its for you to evaluate whether it meets your needs. But if you care that much about efficiency, it seems a strong argument to just use database links and save yourself all this hassle...

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