AMIS Oracle and Java Blog » Anton Scheffer https://technology.amis.nl Friends of Oracle and Java Mon, 29 Jun 2015 14:25:28 +0000 en-US hourly 1 http://wordpress.org/?v=4.2.2 Using an aggregation function to query a JSON-string straight from SQL https://technology.amis.nl/2015/03/13/using-an-aggregation-function-to-query-a-json-string-straight-from-sql/ https://technology.amis.nl/2015/03/13/using-an-aggregation-function-to-query-a-json-string-straight-from-sql/#comments Fri, 13 Mar 2015 21:35:19 +0000 https://technology.amis.nl/?p=34702 Share this on .. Last week I read this blogpost by Scott Wesley. In this post he describes that he uses a custom aggregate function to create large JSON-strings. And for that he used a solution as described in this post by Carsten Czarski. That post of Scott reminded me of a post by my [...]

The post Using an aggregation function to query a JSON-string straight from SQL appeared first on AMIS Oracle and Java Blog.

]]>
Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

Last week I read this blogpost by Scott Wesley. In this post he describes that he uses a custom aggregate function to create large JSON-strings.
And for that he used a solution as described in this post by Carsten Czarski. That post of Scott reminded me of a post by my collegue Lucas Jellema, in which he uses the “normal” listagg aggregation function. When Lucas wrote his post I thought that I could beat the 4000 char limit of his aproach with a custom aggregate function.

I started out with “Tom Kytes stragg_type”, see here, just changed the type of the stragg_type attribute string to clob and the return-type of the functions to clob.

That worked, no problem to aggregate strings of size 4000, no problem for size 10000, but for larger strings it became slower and slower.
Too slow, 15000 bytes took 15 seconds.

So I changed the type back to varchar2, but with a size of varchar2(32767).
The worked, and fast. But only for strings shorter than 29989 bytes. For larger strings I would get a
ORA-22813: operand value exceeds system limits

To solve that I added a clob attribute, just as Carsten Czarski does in his listagg.
Used the varchar2 string for speed, and as soon as it result became to large, the clob for size.
And that worked too. But as soon as the aggregated string size exceeded 58894 bytes the ORA-22813 popped up again.
And as soon as the odciaggregatemerge function used the clob another error: ORA-22922: nonexistent LOB value
So I gave up, 4000 bytes is a nice limit for JSON, if you want something bigger you have to use PL/SQL. So I thought.

But after reading the post of Scott I compared my code with the code of Carsten Czarski to see how he solved my problems.
And it turned out that the first one was easy to solve, just limit the string to 4000 again.
And Carsten’s odciaggregatemerge function will raise a ORA-22922 too. I expect that it is an Oracle bug :)
But, because the odciaggregatemerge function is only executed if the optimizer decides that it will execute the aggregating query parallel, you aggregate very large strings without ever seeing that error.

So, now it’s time to introduce my JSON aggregator. It’s a custom aggregate function, which aggregates a query into a JSON-array. The elements of this array are JSON-objects.

create or replace type agg_json as object
( t_varchar2 varchar2(32767)
, t_clob clob
, static function odciaggregateinitialize( sctx in out agg_json )
  return number
, member function odciaggregateiterate
    ( self in out agg_json
    , a_val dbmsoutput_linesarray
    )
  return number
, member function odciaggregateterminate
    ( self in out agg_json
    , returnvalue out clob
    , flags in number
    )
  return number
, member function odciaggregatemerge
    ( self in out agg_json
    , ctx2 in out agg_json
    )
  return number
, static function json_obj( p_obj dbmsoutput_linesarray )
  return varchar2
, static function escape_json( p_val varchar2 )
  return varchar2
)

Just a type with two attributes, the standard functions for implementing a custom aggregation function, and two supporting static functions.
But notice the a_val parameter of odciaggregateiterate. dbmsoutput_linesarray is a varray of varchar2(32767).
Every name-value pair in the JSON-Object is formed by 3 entries in that varray.
The first entry is the name of the name-value pair.
The second entry is the value of the name-value pair.
And the third is a indicator for the value, is it a string or not.
The fourth entry is the name of the second name-value pair.
The fifth entry is the value of the second name-value pair.

After creating the aggregation function you can create JSON

create or replace function json_agg( agg dbmsoutput_linesarray )
return clob
parallel_enable aggregate using agg_json;

For example, this query

select json_agg( dbmsoutput_linesarray( 'id', level, 'n'
                                      , 'name', level, '' 
                                      , 'test', 'test' || level, ''
                                      )
               ) 
from dual
connect by level <= 3

produces this JSON

 [{"id":1,"name":"1","test":"test1"}
,{"id":2,"name":"2","test":"test2"}
,{"id":3,"name":"3","test":"test3"}]

And to get the JSON from Lucas example nest two calls to this new aggregation function

select agg_json.json_obj
         ( dbmsoutput_linesarray( 'company'
                                , json_agg( dbmsoutput_linesarray( 'name', d.dname, ''
                                                                 , 'identifier', d.deptno, '' 
                                                                 , 'location', d.loc, '' 
                                                                 , 'employees', json_agg( dbmsoutput_linesarray( 'name', e.ename, ''
                                                                                                               , 'job', e.job, ''
                                                                                                               , 'salary', e.sal, 'n'
                                                                                                               , 'manager', nvl( ( select agg_json.json_obj( dbmsoutput_linesarray( 'name', m.ename, ''
                                                                                                                                                                                  , 'salary', m.sal, 'n'
                                                                                                                                                                                  , 'hiredate', to_char( m.hiredate, 'DD-MM-YYYY' ), ''
                                                                                                                                                                                  )
                                                                                                                                                           )
                                                                                                                                   from emp m
                                                                                                                                   where m.empno = e.mgr
                                                                                                                                 ), '{}' ), 'o'
                                                                                                               , 'hiredate', to_char( e.hiredate, 'DD-MM-YYYY' ), '' 
                                                                                                               ) 
                                                                                        ), 'a'
                                          )
                                          )
                                , 'a'
                                )
         )
from dept d
   , emp e
where d.deptno = e.deptno
group by d.dname, d.deptno, d.loc

Here is the code.

Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

The post Using an aggregation function to query a JSON-string straight from SQL appeared first on AMIS Oracle and Java Blog.

]]>
https://technology.amis.nl/2015/03/13/using-an-aggregation-function-to-query-a-json-string-straight-from-sql/feed/ 0
FTPS with PL/SQL https://technology.amis.nl/2014/10/01/ftps-plsql/ https://technology.amis.nl/2014/10/01/ftps-plsql/#comments Wed, 01 Oct 2014 11:08:43 +0000 http://technology.amis.nl/?p=32436 Share this on .. Doing a FTP-job with PL/SQL is not difficult. A basic implementation of RFC 959 can be written in a few hundred lines. See for instance ORACLE-BASE, How to FTP with Oracle PL/SQL or Oracle FAQ’s But what if you want to secure your FTP transmission. Google doesn’t find any pure PL/SQL [...]

The post FTPS with PL/SQL appeared first on AMIS Oracle and Java Blog.

]]>
Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

Doing a FTP-job with PL/SQL is not difficult.
A basic implementation of RFC 959 can be written in a few hundred lines. See for instance ORACLE-BASE, How to FTP with Oracle PL/SQL or Oracle FAQ’s
But what if you want to secure your FTP transmission. Google doesn’t find any pure PL/SQL solutions, only java solutions with a PL/SQL wrapper.
Securing FTP transmission can be done in two ways: SFTP and FTPS.
SFTP stands for SSH File Transfer Protocol. This protocol is not based on the plain FTP protocol, implementing this in PL/SQL is not more difficult, it’s just a lot more work. You will not find anything about SFTP in this posting, maybe I will talk about that later.
FTPS stands for FTP Secure, RFC 4217 – Securing FTP with TLS. And it’s secured with the TLS protocol. Oracle has added support for this protocol to utl_tcp in version 11.2.0.2, UTP_TCP.
As with https you need a certificate (chain) of the server in a Oracle Wallet.
You have pass the path of this wallet to the utl_tcp.open_connection procedure

t_conn := utl_tcp.open_connection( p_host
, p_port
, wallet_path => p_wallet_path
, wallet_password => p_wallet_password
); -- open connection

And you tell the server that you want to secure the connection

write_cmd( t_conn, 'AUTH', 'TLS' )

If the server is OK with that, do it.

utl_tcp.secure_connection( t_conn );

That’s enough to secure the control channel of the FTP connection!

Doing the same for the data channel requires just two more commands

write_cmd( t_conn, 'PBSZ', '0' )
write_cmd( t_conn, 'PROT', 'P' )

A complete example:

declare
  t_conn utl_tcp.connection;
  t_blob blob;
  t_clob clob;
  t_reply number;
  type tp_dir_listing is table of varchar2(32767) index by pls_integer;
  t_dir_listing tp_dir_listing;
--
  g_response varchar2(1000 char);
  g_wallet_path     varchar2(1000 char);
  g_wallet_password varchar2(1000 char);
--
  function read_reply( p_conn in out nocopy utl_tcp.connection, p_verbose boolean := true )
  return number
  is
    t_line varchar(32767);
    t_code varchar2(3 char);
  begin
    t_line := utl_tcp.get_line( p_conn, true );
    if nvl( length( t_line ), 0 )  0
       and instr( t_tmp, '(', 1, 2 ) = 0
       and instr( t_tmp, ')' ) > 0
       and instr( t_tmp, ')', 1, 2 ) = 0
       )
    then
      t_tmp := substr( t_tmp, instr( t_tmp, '(' ) + 1 ); 
      t_tmp := substr( t_tmp, 1, instr( t_tmp, ')' ) - 1 );
    else
      t_tmp := translate( t_tmp, '1234567890,' || t_tmp, '1234567890,' );
    end if;      
    t_host := substr( t_tmp, 1, instr( t_tmp, ',', 1, 4 ) - 1 ); 
    t_host := replace( t_host, ',', '.' );
    t_tmp := substr( t_tmp, instr( t_tmp, ',', 1, 4 ) + 1 );
    t_port := to_number( substr( t_tmp, 1, instr( t_tmp, ',' ) - 1 ) );
    t_port := t_port * 256;
    t_port := t_port + to_number( substr( t_tmp, instr( t_tmp, ',' ) + 1 ) );
    t_conn := utl_tcp.open_connection( t_host, t_port, wallet_path => g_wallet_path, wallet_password => g_wallet_password );
    return t_conn; 
  end; 
--
  function write_cmd( p_conn in out nocopy utl_tcp.connection, p_cmd varchar2, p_param varchar2 := '' )
  return number
  is
    t_dummy pls_integer;
  begin
    t_dummy := utl_tcp.write_line( p_conn, p_cmd || rtrim( ' ' || p_param ) );
    return read_reply( p_conn );
  end;
--
  function open_data_channel( p_conn in out nocopy utl_tcp.connection )
  return utl_tcp.connection
  is
    t_tmp varchar2(32767); 
    t_host varchar2(1000 char); 
    t_port pls_integer; 
    t_conn utl_tcp.connection;
  begin
    if write_cmd( p_conn, 'PASV' ) != 227
    then
      raise_application_error( -20001, 'Could not set PASSIVE: ' || g_response, true );
    end if;
    t_tmp := g_response;
    if (   instr( t_tmp, '(' ) > 0
       and instr( t_tmp, '(', 1, 2 ) = 0
       and instr( t_tmp, ')' ) > 0
       and instr( t_tmp, ')', 1, 2 ) = 0
       )
    then
      t_tmp := substr( t_tmp, instr( t_tmp, '(' ) + 1 ); 
      t_tmp := substr( t_tmp, 1, instr( t_tmp, ')' ) - 1 );
    else
      t_tmp := translate( t_tmp, '1234567890,' || t_tmp, '1234567890,' );
    end if;      
    t_host := substr( t_tmp, 1, instr( t_tmp, ',', 1, 4 ) - 1 ); 
    t_host := replace( t_host, ',', '.' );
    t_tmp := substr( t_tmp, instr( t_tmp, ',', 1, 4 ) + 1 );
    t_port := to_number( substr( t_tmp, 1, instr( t_tmp, ',' ) - 1 ) );
    t_port := t_port * 256;
    t_port := t_port + to_number( substr( t_tmp, instr( t_tmp, ',' ) + 1 ) );
    t_conn := utl_tcp.open_connection( t_host, t_port, wallet_path => g_wallet_path, wallet_password => g_wallet_password );
    return t_conn; 
  end; 
--
  function login
    ( p_host varchar2
    , p_usr  varchar2 := null
    , p_pw   varchar2 := null
    , p_port number := 21
    , p_account varchar2 := null
    , p_wallet_path varchar2 := null
    , p_wallet_password varchar2 := null
    )
  return utl_tcp.connection
  is
    t_reply number;
    t_conn utl_tcp.connection;
    t_usr varchar2(1000 char);
    t_pw  varchar2(1000 char);
    t_acc varchar2(1000 char);
  begin
    t_conn := utl_tcp.open_connection( p_host, p_port, wallet_path => p_wallet_path, wallet_password => p_wallet_password ); -- open connection
    case read_reply( t_conn ) 
      when 120
      then
        raise_application_error( -20001, 'FTP server not ready: ' || g_response, true );
      when 421
      then
        raise_application_error( -20001, 'FTP server not available: ' || g_response, true );
      else
        null;
    end case;
    if write_cmd( t_conn, 'AUTH', 'TLS' ) = 234 
    then
      utl_tcp.secure_connection( t_conn );
      if     write_cmd( t_conn, 'PBSZ', '0' ) = 200  
         and write_cmd( t_conn, 'PROT', 'P' ) = 200
      then
        g_wallet_path     := p_wallet_path;
        g_wallet_password := p_wallet_password;
      end if;
    end if;
    if p_usr is null
    then
      t_usr := 'anonymous';
      t_pw  := 'anonymous@mysite.com';  
      t_acc := '*********';
    else  
      t_usr := p_usr;
      t_pw  := p_pw;  
      t_acc := p_account;
    end if;
    t_reply := write_cmd( t_conn, 'USER', t_usr );  
    if t_reply = 331
    then
      t_reply := write_cmd( t_conn, 'PASS', t_pw );  
    end if;  
    if t_reply = 332
    then
      t_reply := write_cmd( t_conn, 'ACCT', t_acc );  
    end if;  
    if t_reply not in ( 230, 202 )
    then
      raise_application_error( -20001, 'Could not log in: ' || g_response, true );
    end if;
    return t_conn;
  end;
--
  procedure get_file
    ( p_conn in out nocopy utl_tcp.connection
    , p_path in varchar2
    , p_file in out blob
    )
  is
    t_reply number;
    t_buf raw(32767);
    t_cnt pls_integer;
    t_conn utl_tcp.connection;
  begin
    t_reply := write_cmd( p_conn, 'TYPE', 'I' );    
    t_conn := open_data_channel( p_conn );
    t_reply := write_cmd( p_conn, 'RETR', p_path );    
    if t_reply in ( 125, 150 )
    then
      if g_wallet_path is not null
      then
        utl_tcp.secure_connection( t_conn );
      end if;
      dbms_lob.createtemporary( p_file, true );
      begin 
        loop
          t_cnt := utl_tcp.read_raw( t_conn, t_buf, 32767 );
          dbms_lob.writeappend( p_file, t_cnt, t_buf );
        end loop;
      exception
        when utl_tcp.end_of_input
        then
          null;
      end;
      utl_tcp.close_connection( t_conn );
      t_reply := read_reply( p_conn );
    end if;
    if t_reply not in ( 226, 250 )
    then
      raise_application_error( -20001, 'Could not retrieve file: ' || g_response, true );
    end if;
  end;
--
  procedure get_file
    ( p_conn in out nocopy utl_tcp.connection
    , p_path in varchar2
    , p_file in out clob
    )
  is
    t_reply number;
    t_buf varchar2(32767);
    t_cnt pls_integer;
    t_conn utl_tcp.connection;
  begin
    t_reply := write_cmd( p_conn, 'TYPE', 'A' );    
    t_conn := open_data_channel( p_conn );
    t_reply := write_cmd( p_conn, 'RETR', p_path );    
    if t_reply in ( 125, 150 )
    then
      if g_wallet_path is not null
      then
        utl_tcp.secure_connection( t_conn );
      end if;
      dbms_lob.createtemporary( p_file, true );
      begin 
        loop
          t_buf := utl_i18n.raw_to_char( utl_tcp.get_raw( t_conn, 32767 ), 'US7ASCII' );
          dbms_lob.writeappend( p_file, length( t_buf ), t_buf );
        end loop;
      exception
        when utl_tcp.end_of_input
        then
          null;
      end;
      utl_tcp.close_connection( t_conn );
      t_reply := read_reply( p_conn );
    end if;
    if t_reply not in ( 226, 250 )
    then
      raise_application_error( -20001, 'Could not retrieve file: ' || g_response, true );
    end if;
  end;
--
  procedure nlst
    ( p_conn in out nocopy utl_tcp.connection
    , p_dir_listing in out tp_dir_listing
    , p_path in varchar2 := null
    )
  is
    t_reply number;
    t_conn utl_tcp.connection;
  begin
    p_dir_listing.delete;
    t_reply := write_cmd( p_conn, 'TYPE', 'A' );    
    t_conn := open_data_channel( p_conn );
    t_reply := write_cmd( p_conn, 'NLST', p_path );    
    if t_reply in ( 125, 150 )
    then
      if g_wallet_path is not null
      then
        utl_tcp.secure_connection( t_conn );
      end if;
      begin 
        loop
          p_dir_listing( p_dir_listing.count + 1 ) := utl_tcp.get_line( t_conn, true ); 
        end loop;
      exception
        when utl_tcp.end_of_input
        then
          null;
      end;
      utl_tcp.close_connection( t_conn );
      t_reply := read_reply( p_conn );
    end if;
    if t_reply != 226
    then
      raise_application_error( -20001, 'Could not get NLST: ' || g_response, true );
    end if;
  end;
--
begin
  t_conn := login( 'localhost', p_wallet_path => 'file:C:\oracle\wallet', p_wallet_password => 'amis.rules.again' );
--  get_file( t_conn, '/my_dir/my_ascii_file.txt', t_clob );  
  nlst( t_conn, t_dir_listing, '*.txt' );  
  t_reply := write_cmd( t_conn, 'QUIT' );
  utl_tcp.close_connection( t_conn );
end;

You will find the same example here

Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

The post FTPS with PL/SQL appeared first on AMIS Oracle and Java Blog.

]]>
https://technology.amis.nl/2014/10/01/ftps-plsql/feed/ 2
Solving PLS-00753: malformed or corrupted wrapped unit within Apex SQL Workshop https://technology.amis.nl/2013/10/17/solving-pls-00753-malformed-or-corrupted-wrapped-unit-within-apex-sql-workshop/ https://technology.amis.nl/2013/10/17/solving-pls-00753-malformed-or-corrupted-wrapped-unit-within-apex-sql-workshop/#comments Thu, 17 Oct 2013 12:52:31 +0000 http://technology.amis.nl/?p=24893 Share this on .. I was working on this great Apex plugin to load Excel sheets with more than 50 columns into the database. And because I had all those great, but secret ideas, to solve all the problems I used a wrapped package to store all the functionality of the plugin. That worked perfect [...]

The post Solving PLS-00753: malformed or corrupted wrapped unit within Apex SQL Workshop appeared first on AMIS Oracle and Java Blog.

]]>
Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

I was working on this great Apex plugin to load Excel sheets with more than 50 columns into the database. And because I had all those great, but secret ideas, to solve all the problems I used a wrapped package to store all the functionality of the plugin.
That worked perfect on my development machine, were I release all the code with SQL*Plus or Toad, but not on apex.oracle.com.
When I uploaded my scripts to the SQL Workshop, and ran it the wrapped script resulted in an error:

Error at line 0: PLS-00753: malformed or corrupted wrapped unit.

Googling this error didn’t help. Oracle advises to run the unwrapped code and wrap it afterwards inside the database, not much of a solution if you want to keep the source secret. But I found this site, which says: “This only happens if the last character of the wrapped code is at the end of a line.” The solution from that site didn’t worked for me, but I started to play with modifying the source of my invalid package.

Running this script after the wrapped script worked for me:

begin
execute immediate replace
( dbms_metadata.get_ddl( 'PACKAGE_BODY','DLW', sys_context( 'userenv', 'current_schema' ) )
, ' ' || chr(10)
, chr(10)
);
end;

Just remove a trailing space of every line. And if you happen to be on an Oracle 10 database and have a large wrapped package, this will work too:

declare
t_ddl clob;
t_ddl_tab dbms_sql.varchar2s;
t_cur integer;
begin
t_ddl := replace( dbms_metadata.get_ddl( 'PACKAGE_BODY','DLW', sys_context( 'userenv', 'current_schema' ) )
, ' ' || chr(10)
, chr(10)
);
for i in 0 .. trunc( length( t_ddl ) - 1 ) / 256
loop
t_ddl_tab( i ) := substr( t_ddl, 1 + i * 256, 256 );
end loop;
t_cur := dbms_sql.open_cursor;
dbms_sql.parse( t_cur, t_ddl_tab, 0, t_ddl_tab.last(), false, dbms_sql.native );
dbms_sql.close_cursor( t_cur );
end;

By the way, you can find this great plugin here

Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

The post Solving PLS-00753: malformed or corrupted wrapped unit within Apex SQL Workshop appeared first on AMIS Oracle and Java Blog.

]]>
https://technology.amis.nl/2013/10/17/solving-pls-00753-malformed-or-corrupted-wrapped-unit-within-apex-sql-workshop/feed/ 1
Read an Excel xlsx with PL/SQL https://technology.amis.nl/2013/01/19/read-a-excel-xlsx-with-plsql/ https://technology.amis.nl/2013/01/19/read-a-excel-xlsx-with-plsql/#comments Sat, 19 Jan 2013 20:12:04 +0000 http://technology.amis.nl/?p=20612 Share this on .. At the OTN SQL and PLSQL forum I promised to publish some code I use for a project I’ still working on. This code allows you to select the content from an Excel document select * from table( as_read_xlsx.read( as_read_xlsx.file2blob( 'DOC', 'Book1.xlsx' ) ) ); SHEET_NR SHEET_NAME ROW_NR COL_NR CELL CEL [...]

The post Read an Excel xlsx with PL/SQL appeared first on AMIS Oracle and Java Blog.

]]>
Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

At the OTN SQL and PLSQL forum I promised to publish some code I use for a project I’ still working on. This code allows you to select the content from an Excel document

select * from table( as_read_xlsx.read( as_read_xlsx.file2blob( 'DOC', 'Book1.xlsx' ) ) );

  SHEET_NR SHEET_NAME ROW_NR COL_NR CELL  CEL STRING_VAL NUMBER_VAL DATE_VAL
---------- ---------- ------ ------ ----- --- ---------- ---------- --------------------------
         1 Mijn naam       1      1 A1    N                      11
         1 Mijn naam       1      2 B1    N                      12
         1 Mijn naam       1      3 C1    N                      13
         1 Mijn naam       2      1 A2    N                      21
         1 Mijn naam       2      2 B2    N                      22
         1 Mijn naam       2      3 C2    N                      23
         1 Mijn naam       3      1 A3    N                      31
         1 Mijn naam       3      2 B3    N                      32
         1 Mijn naam       3      3 C3    N                      33
         1 Mijn naam       4      4 D4    S   D4
         1 Mijn naam       6      2 B6    D
         1 Mijn naam       7      2 B7    D
         1 Mijn naam       8      2 B8    D
         1 Mijn naam       9      2 B9    D
         1 Mijn naam      10      2 B10   D
         2 Sheet3          2      2 B2    S   Test
         2 Sheet3          3      3 C3    D                         19-JAN-2013 20:17:00
         2 Sheet3          4      1 A4    S   Anton

18 rows selected.

So here it is

Anton

** Changelog:
** 18-02-2013 – Ralph Bieber
Handle cell type “str” to prevent ORA-06502
if cell content is a string calculated by formula,
then cell type is “str” instead of “s” and value is inside tag
** 19-02-2013 – Ralph Bieber
Add column formula in tp_one_cell record, to show, if value is calculated by formula
** 20-02-2013 – Anton Scheffer
Handle cell types ‘inlineStr’ and ‘e’ to prevent ORA-06502
** 19-03-2013 – Anton Scheffer
Support for formatted and empty strings
Handle columns per row to prevent ORA-31186: Document contains too many nodes
** 12-06-2013 – Anton Scheffer
Handle sharedStrings.xml on older Oracle database versions
** 18-09-2013 – Anton Scheffer
** 18-09-2013 – Anton Scheffer
Fix for LPX-00200 could not convert from encoding UTF-8 to …
(Note, this is an error I can’t reproduce myself, maybe depending on database version and characterset)
Thank you Stanislav Safonov for this solution
Handle numbers with scientific notation
** 20-01-2014 – Anton Scheffer
Fix for a large number (60000+) of strings
** 16-05-2014 – Anton Scheffer
round to 15 digits

Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

The post Read an Excel xlsx with PL/SQL appeared first on AMIS Oracle and Java Blog.

]]>
https://technology.amis.nl/2013/01/19/read-a-excel-xlsx-with-plsql/feed/ 74
Select a blob across a database link, without getting ORA-22992 https://technology.amis.nl/2012/07/02/select-a-blob-across-a-database-link-without-getting-ora-22992/ https://technology.amis.nl/2012/07/02/select-a-blob-across-a-database-link-without-getting-ora-22992/#comments Mon, 02 Jul 2012 12:19:56 +0000 http://technology.amis.nl/?p=18352 Share this on .. Just a quick blog about a simple trick to select a blob across a database link, especially for a collegue of mine, Harry Dragstra. Say, you have a table with a blob on a remote database SQL> describe test_blob@xx Name Null? Type ----------------------------------------- -------- ---------------------------- ID NUMBER BLB BLOB When you [...]

The post Select a blob across a database link, without getting ORA-22992 appeared first on AMIS Oracle and Java Blog.

]]>
Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

Just a quick blog about a simple trick to select a blob across a database link, especially for a collegue of mine, Harry Dragstra.

Say, you have a table with a blob on a remote database

SQL> describe test_blob@xx
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 ID                                                 NUMBER
 BLB                                                BLOB

When you use a normal select statement to get all columns you run into an error

SQL> select id, blb
  2  from test_blob@xx;

ERROR:
ORA-22992: cannot use LOB locators selected from remote tables

no rows selected

But when you wrap your blob column in a scalar subquery expression it works like a charm

SQL> select id
  2       , ( select blb from dual ) blb
  3       , dbms_lob.getlength( ( select blb from dual ) ) len
  4       , utl_raw.cast_to_varchar2( dbms_lob.substr( ( select blb from dual ), 20, 1 ) ) sub
  5  from test_blob@xx;

        ID BLB
---------- ------------------------------------------------------------
       LEN
----------
SUB
--------------------------------------------------------------------------------
         1 74657374696E6774657374696E6774657374696E6774657374696E677465
      7000
testingtestingtestin

         2 74657374696E6774657374696E6774657374696E6774657374696E677465
   7168000
testingtestingtestin

This trick works, as far as I know, only on a 11.2 database.
P.S. According to Tom Kyte this method works with databases from version 10.2.0.5.0 and upwards, but beware, see Ask Tom

Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

The post Select a blob across a database link, without getting ORA-22992 appeared first on AMIS Oracle and Java Blog.

]]>
https://technology.amis.nl/2012/07/02/select-a-blob-across-a-database-link-without-getting-ora-22992/feed/ 5
Generating a PDF-document with some plsql: as_pdf_mini => as_pdf3 https://technology.amis.nl/2012/04/11/generating-a-pdf-document-with-some-plsql-as_pdf_mini-as_pdf3/ https://technology.amis.nl/2012/04/11/generating-a-pdf-document-with-some-plsql-as_pdf_mini-as_pdf3/#comments Wed, 11 Apr 2012 21:04:21 +0000 http://technology.amis.nl/?p=17718 Share this on .. It has been more than a year since I published my previous blog on generating PDF with pl/sql. In that time I’ve rewritten as_pdf two times, so now its time for as_pdf3 The most important improvement is Truetype Fonts declare x pls_integer; begin as_pdf3.init; as_pdf3.write( 'But others fonts and encodings are [...]

The post Generating a PDF-document with some plsql: as_pdf_mini => as_pdf3 appeared first on AMIS Oracle and Java Blog.

]]>
Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

It has been more than a year since I published my previous blog on generating PDF with pl/sql.
In that time I’ve rewritten as_pdf two times, so now its time for as_pdf3

The most important improvement is Truetype Fonts

declare
x pls_integer;
begin
as_pdf3.init;
as_pdf3.write( 'But others fonts and encodings are possible using TrueType fontfiles.' );
x := as_pdf3.load_ttf_font( 'MY_FONTS', 'refsan.ttf', 'CID', p_compress => false );
as_pdf3.set_font( x, 12 );
as_pdf3.write( 'The Windows MSReference SansSerif font contains a lot of encodings, for instance', -1, 700 );
as_pdf3.set_font( x, 15 );
as_pdf3.write( 'Albanian: Kush mund të lexoni këtë diçka si kjo', -1, -1 );
as_pdf3.write( 'Croatic: Tko može čitati to nešto poput ovoga', -1, -1 );
as_pdf3.write( 'Russian: Кто может прочитать это что-то вроде этого', -1, -1);
as_pdf3.write( 'Greek: Ποιος μπορεί να διαβάσει αυτό το κάτι σαν αυτό', -1, -1 );
--
as_pdf3.set_font( 'helvetica', 12 );
as_pdf3.write( 'Or by using a TrueType collection file (ttc).', -1, 600 );
as_pdf3.load_ttc_fonts( 'MY_FONTS', 'cambria.ttc', p_embed => true, p_compress => false );
as_pdf3.set_font( 'cambria', 15 ); -- font family
as_pdf3.write( 'Anton, testing 1,2,3 with Cambria', -1, -1 );
as_pdf3.set_font( 'CambriaMath', 15 ); -- fontname
as_pdf3.write( 'Anton, testing 1,2,3 with CambriaMath', -1, -1 );
--
as_pdf3.set_font( 'helvetica', 12 );
as_pdf3.write( 'Or if you need to generate a PDF report in Chinese:', -1, 520 );
as_pdf3.set_font( as_pdf3.load_ttf_font( 'MY_DIR', 'simfang.ttf', 'CID', p_compress => false ), 12 );
as_pdf3.write( 'Chinese: 在中国的一个简单的句子', -1, -1 );
--
as_pdf3.save_pdf;
end;

fonts

But some more things, headers en footers:

declare
t_rc sys_refcursor;
t_query varchar2(1000);
begin
as_pdf3.init;
as_pdf3.load_ttf_font( 'MY_FONTS', 'COLONNA.TTF', 'CID' );
as_pdf3.set_page_proc( q'~
begin
as_pdf3.set_font( 'helvetica', 8 );
as_pdf3.put_txt( 10, 15, 'Page #PAGE_NR# of "PAGE_COUNT#' );
as_pdf3.set_font( 'helvetica', 12 );
as_pdf3.put_txt( 350, 15, 'This is a footer text' );
as_pdf3.set_font( 'helvetica', 'B', 15 );
as_pdf3.put_txt( 200, 780, 'This is a header text' );
as_pdf3.put_image( 'MY_DIR', 'amis.jpg', 500, 15 );
end;~' );
as_pdf3.set_page_proc( q'~
begin
as_pdf3.set_font( 'Colonna MT', 'N', 50 );
as_pdf3.put_txt( 150, 200, 'Watermark Watermark Watermark', 60 );
end;~' );
t_query := 'select rownum, sysdate + level, ''example'' || level from dual connect by level as_pdf3.query2table( t_query );
open t_rc for t_query;
as_pdf3.refcursor2table( t_rc );
as_pdf3.save_pdf;
end;

And the code for this package: as_pdf3

See also this blog for some additions.

** Changelog:
** Date: 13-08-2012
** added two procedure for Andreas Weiden
** see https://sourceforge.net/projects/pljrxml2pdf/
** Date: 16-04-2012
** changed code for parse_png
** Date: 15-04-2012
** only dbms_lob.freetemporary for temporary blobs
** Date: 11-04-2012
** Initial release of as_pdf3

Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

The post Generating a PDF-document with some plsql: as_pdf_mini => as_pdf3 appeared first on AMIS Oracle and Java Blog.

]]>
https://technology.amis.nl/2012/04/11/generating-a-pdf-document-with-some-plsql-as_pdf_mini-as_pdf3/feed/ 153
Create an Excel-file with PL/SQL https://technology.amis.nl/2011/02/19/create-an-excel-file-with-plsql/ https://technology.amis.nl/2011/02/19/create-an-excel-file-with-plsql/#comments Sat, 19 Feb 2011 15:24:46 +0000 http://technology.amis.nl/blog/?p=10995 Share this on .. For this project I took an Apex-plugin I have written, (IR) Report to Excel (xlsx), and turned it into a PL/SQL package. With this package it’s very easy to create an Excel 2007 file with only a few lines of PL/SQL code. begin as_xlsx.query2sheet( 'select * from dual' ); as_xlsx.save( 'MY_DIR', [...]

The post Create an Excel-file with PL/SQL appeared first on AMIS Oracle and Java Blog.

]]>
Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

For this project I took an Apex-plugin I have written, (IR) Report to Excel (xlsx), and turned it into a PL/SQL package. With this package it’s very easy to create an Excel 2007 file with only a few lines of PL/SQL code.

begin
as_xlsx.query2sheet( 'select * from dual' );
as_xlsx.save( 'MY_DIR', 'my.xlsx' );
end;

The main purpose for this package is getting data from the database into an Excel file, so I deliberate did not include some Excel functionality, such as formulas, into the package. Excel itself is a far better tool for such things.

Anton

The source code for the package: as_xlsx
P.S. I have added the possibility to add Comments and MergedCells to the Excel-file
P.S.2 And bold/italic fonts
P.S.3 Fixed issue with timezones with a regionname
P.S.4 Fixed issue with XML-escaping from text
P.S.5 Fixed NLS-issue with column width
P.S.6 Added p_rgb to get_font
P.S.7 Fixed bug in add_string
P.S.8 Fixed set_autofilter (only one autofilter per sheet, added _xlnm._FilterDatabase)
Added list_validation = drop-down
P.S.9 Added freeze_pane

Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

The post Create an Excel-file with PL/SQL appeared first on AMIS Oracle and Java Blog.

]]>
https://technology.amis.nl/2011/02/19/create-an-excel-file-with-plsql/feed/ 136
AS_PDF, generating a PDF-document with some plsql https://technology.amis.nl/2010/10/20/as_pdf-generating-a-pdf-document-with-some-plsql/ https://technology.amis.nl/2010/10/20/as_pdf-generating-a-pdf-document-with-some-plsql/#comments Wed, 20 Oct 2010 20:28:06 +0000 http://technology.amis.nl/blog/?p=8650 Share this on .. I’ve written a small package (1500 lines). But with this package you can generate a PDF-document with a few lines of PL/SQL code. It’s small because it lacks some functionality. It can only use the standard PDF fonts, and that means that it can only use the WINDOWS-1252 encoding/characterset. But besides [...]

The post AS_PDF, generating a PDF-document with some plsql appeared first on AMIS Oracle and Java Blog.

]]>
Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

I’ve written a small package (1500 lines). But with this package you can generate a PDF-document with a few lines of PL/SQL code. It’s small because it lacks some functionality. It can only use the standard PDF fonts, and that means that it can only use the WINDOWS-1252 encoding/characterset. But besides that it’s fairly complete.

For instance.

begin
as_pdf_mini.init;
as_pdf_mini.write( 'Some text with a newline-character included at this "
" place.' );
as_pdf_mini.write( 'Normally text written with as_pdf_mini.write() is appended after the previous text. But the text wraps automaticly to a new line.' );
as_pdf_mini.write( 'But you can place your text at any place', -1, 700 );
as_pdf_mini.write( 'you want', 100, 650 );
as_pdf_mini.write( 'You can even align it, left, right, or centered', p_y =&gt; 600, p_alignment => 'right' );
as_pdf_mini.save_pdf;
end;

Some text

And

begin
as_pdf_mini.init;
for i in 1 .. 10
loop
as_pdf_mini.horizontal_line( 30, 700 - i * 15, 100, i );
end loop;
for i in 1 .. 10
loop
as_pdf_mini.vertical_line( 150 + i * 15, 700, 100, i );
end loop;
for i in 0 .. 255
loop
as_pdf_mini.horizontal_line( 330, 700 - i, 100, p_line_color =&gt; to_char( i, 'fm0x' ) || to_char( i, 'fm0x' ) || to_char( i, 'fm0x' ) );
end loop;
as_pdf_mini.save_pdf;
end;

Some lines

And jpg and png images, from a url, the file-system or from a database blob can be included:

Example of an image

And to show the standard PDF fonts

begin
as_pdf_mini.init;
as_pdf_mini.write( 'The mini version of AS_PDF is restricted to the 14 standard PDF-fonts and the WINDOWS-1252 encoding.' );
as_pdf_mini.set_font( 'helvetica' );
as_pdf_mini.write( 'helvetica, normal: ' || 'The quick brown fox jumps over the lazy dog. 1234567890', -1, 700 );
as_pdf_mini.set_font( 'helvetica', 'I' );
as_pdf_mini.write( 'helvetica, italic: ' || 'The quick brown fox jumps over the lazy dog. 1234567890', -1, -1 );
as_pdf_mini.set_font( 'helvetica', 'b' );
as_pdf_mini.write( 'helvetica, bold: ' || 'The quick brown fox jumps over the lazy dog. 1234567890', -1, -1 );
as_pdf_mini.set_font( 'helvetica', 'BI' );
as_pdf_mini.write( 'helvetica, bold italic: ' || 'The quick brown fox jumps over the lazy dog. 1234567890', -1, -1 );
as_pdf_mini.set_font( 'times' );
as_pdf_mini.write( 'times, normal: ' || 'The quick brown fox jumps over the lazy dog. 1234567890', -1, 625 );
as_pdf_mini.set_font( 'times', 'I' );
as_pdf_mini.write( 'times, italic: ' || 'The quick brown fox jumps over the lazy dog. 1234567890', -1, -1 );
as_pdf_mini.set_font( 'times', 'b' );
as_pdf_mini.write( 'times, bold: ' || 'The quick brown fox jumps over the lazy dog. 1234567890', -1, -1 );
as_pdf_mini.set_font( 'times', 'BI' );
as_pdf_mini.write( 'times, bold italic: ' || 'The quick brown fox jumps over the lazy dog. 1234567890', -1, -1 );
as_pdf_mini.set_font( 'courier' );
as_pdf_mini.write( 'courier, normal: ' || 'The quick brown fox jumps over the lazy dog. 1234567890', -1, 550 );
as_pdf_mini.set_font( 'courier', 'I' );
as_pdf_mini.write( 'courier, italic: ' || 'The quick brown fox jumps over the lazy dog. 1234567890', -1, -1 );
as_pdf_mini.set_font( 'courier', 'b' );
as_pdf_mini.write( 'courier, bold: ' || 'The quick brown fox jumps over the lazy dog. 1234567890', -1, -1 );
as_pdf_mini.set_font( 'courier', 'BI' );
as_pdf_mini.write( 'courier, bold italic: ' || 'The quick brown fox jumps over the lazy dog. 1234567890', -1, -1 );
--
as_pdf_mini.set_font( 'courier' );
as_pdf_mini.write( 'symbol:', -1, 475 );
as_pdf_mini.set_font( 'symbol' );
as_pdf_mini.write( 'The quick brown fox jumps over the lazy dog. 1234567890', -1, -1 );
as_pdf_mini.set_font( 'courier' );
as_pdf_mini.write( 'zapfdingbats:', -1, -1 );
as_pdf_mini.set_font( 'zapfdingbats' );
as_pdf_mini.write( 'The quick brown fox jumps over the lazy dog. 1234567890', -1, -1 );
--
as_pdf_mini.set_font( 'times', 'N', 20 );
as_pdf_mini.write( 'times, normal with fontsize 20pt', -1, 400 );
as_pdf_mini.set_font( 'times', 'N', 6 );
as_pdf_mini.write( 'times, normal with fontsize 5pt', -1, -1 );
as_pdf_mini.save_pdf;
end;

Fonts

Or you can write a procedure: refcursor in, pdf out

Refcursor

The package: as_pdf_mini
But see also it successor as_pdf3

Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

The post AS_PDF, generating a PDF-document with some plsql appeared first on AMIS Oracle and Java Blog.

]]>
https://technology.amis.nl/2010/10/20/as_pdf-generating-a-pdf-document-with-some-plsql/feed/ 92
Parsing a Microsoft Word docx, and unzip zipfiles, with PL/SQL https://technology.amis.nl/2010/06/09/parsing-a-microsoft-word-docx-and-unzip-zipfiles-with-plsql/ https://technology.amis.nl/2010/06/09/parsing-a-microsoft-word-docx-and-unzip-zipfiles-with-plsql/#comments Wed, 09 Jun 2010 18:46:19 +0000 http://technology.amis.nl/blog/?p=8090 Share this on .. Some days ago a collegue of mine asked if I could made something for him to unzip a Microsoft Word 2007 docx file. And of course in the database and without using Java. As it turns out, a docx file is just a ordinary zipfile, with some xml-files stored in it. [...]

The post Parsing a Microsoft Word docx, and unzip zipfiles, with PL/SQL appeared first on AMIS Oracle and Java Blog.

]]>
Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

Some days ago a collegue of mine asked if I could made something for him to unzip a Microsoft Word 2007 docx file. And of course in the database and without using Java.
As it turns out, a docx file is just a ordinary zipfile, with some xml-files stored in it. And because I already had build a little procedure to make zipfiles some weeks ago it didn’t took me not more than 3 hours to build a package to unzip a zipfile from PL/SQL.
With this package you can get list of all the files in a zipfile, and unzip a file if you want. And if you know a little xml you can query the text form your Word document.

Say you have a Word document like this

Then you can query the text from it like this

As you can see the text is shown twice, I didn’t put time in trying to understand the Word format. I leave that to somebody else.

Anton

And here’s a link with the used code: as_zip
package with zip and unzip

** Changelog:
** Date: 29-04-2012
** fixed bug for large uncompressed files, thanks Morten Braten
** Date: 21-03-2012
** Take CRC32, compressed length and uncompressed length from
** Central file header instead of Local file header
** Date: 17-02-2012
** Added more support for non-ascii filenames
** Date: 25-01-2012
** Added MIT-license
** Date: 31-01-2014
** file limit increased to 4GB

Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

The post Parsing a Microsoft Word docx, and unzip zipfiles, with PL/SQL appeared first on AMIS Oracle and Java Blog.

]]>
https://technology.amis.nl/2010/06/09/parsing-a-microsoft-word-docx-and-unzip-zipfiles-with-plsql/feed/ 13
Utl_compress, gzip and zlib https://technology.amis.nl/2010/03/13/utl_compress-gzip-and-zlib/ https://technology.amis.nl/2010/03/13/utl_compress-gzip-and-zlib/#comments Sat, 13 Mar 2010 19:38:08 +0000 http://technology.amis.nl/blog/?p=7626 Share this on .. Oracle has a a supplied package utl_compress, which can be used to compress and decompress data with PL/SQL. According to the documentation it uses the “Lempel-Ziv compression algorithme”, and “The output of the UTL_COMPRESS compressed data is compatible with gzip”. That means it’s following the RFC 1952 specs, RFC 1952. And [...]

The post Utl_compress, gzip and zlib appeared first on AMIS Oracle and Java Blog.

]]>
Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

Oracle has a a supplied package utl_compress, which can be used to compress and decompress data with PL/SQL. According to the documentation it uses the “Lempel-Ziv compression algorithme”, and “The output of the UTL_COMPRESS compressed data is compatible with gzip”. That means it’s following the RFC 1952 specs, RFC 1952. And that may be very useful (but I have never used it), but I need compression (I’m working on a PDF-generator in PL/SQL) and decompression (unwrapping wrapped PL/SQL!) in the zlib-format, RFC 1950. Both formats use the same algorithm, RFC 1951, but have different headers and trailers. So can utl_compress be used to for compressing/decompressing data according to the zlib-specs. Yes! In fact all you need is how to calculate the Adler32-checksum, which is used as the trailer of the zlib-format.

  function adler32( p_src in blob )
  return varchar2
  is
    s1 pls_integer := 1;
    s2 pls_integer := 0;
  begin
    for i in 1 .. dbms_lob.getlength( p_src )
    loop
      s1 := mod( s1 + to_number( rawtohex( dbms_lob.substr( p_src, 1, i ) ), 'XX' ), 65521 );
      s2 := mod( s2 + s1, 65521);
    end loop;
    return to_char( s2, 'fm0XXX' ) || to_char( s1, 'fm0XXX' );
  end;

With this Adler32 function compressing it zlib-format is nothing more as a simple wrapper for utl_compress.lz_compress

  function zlib_compress( p_src in blob )
  return blob
  is
    t_tmp blob;
    t_cpr blob;
  begin
    t_tmp := utl_compress.lz_compress( p_src );
    dbms_lob.createtemporary( t_cpr, false );
    t_cpr := hextoraw( '789C' ); -- zlib header
    dbms_lob.copy( t_cpr, t_tmp, dbms_lob.getlength( t_tmp ) - 10 - 8, 3, 11 );
    dbms_lob.append( t_cpr, hextoraw( adler32( p_src ) ) ); -- zlib trailer
    dbms_lob.freetemporary( t_tmp );
    return t_cpr;
  end;

Unwrapping the zlib-format with utl_compress takes just a little more effort. Utl_compress.lz_decompress only works if the input has a correct trailer, which includes a crc32-checksum of the uncompressed data. And that’s something we don’t have and can’t calculate with the data we have. But utl_compress has another procedure lz_uncompress_extract, which can be used to decompress the input byte for byte. And that works without the correct trailer. But only until the last byte of the uncompressed data. If we try to get the last decompressed byte an exception ORA-29294: A data error occurred during compression or uncompression, is raised. But the value of this last byte can derived from the Adler32 checksum we have!

  function zlib_decompress( p_src in blob )
  return blob
  is
    t_out blob;
    t_tmp blob;
    t_buffer raw(1);
    t_hdl binary_integer;
    t_s1 pls_integer; -- s1 part of adler32 checksum
    t_last_chr pls_integer;
  begin
    dbms_lob.createtemporary( t_out, false );
    dbms_lob.createtemporary( t_tmp, false );
    t_tmp := hextoraw( '1F8B0800000000000003' ); -- gzip header
    dbms_lob.copy( t_tmp, p_src, dbms_lob.getlength( p_src ) - 2 - 4, 11, 3 );
    dbms_lob.append( t_tmp, hextoraw( '0000000000000000' ) ); -- add a fake trailer
    t_hdl := utl_compress.lz_uncompress_open( t_tmp );
    t_s1 := 1;
    loop
      begin
        utl_compress.lz_uncompress_extract( t_hdl, t_buffer );
      exception
        when others
        then
          exit;
      end;
      dbms_lob.append( t_out, t_buffer );
      t_s1 := mod( t_s1 + to_number( rawtohex( t_buffer ), 'xx' ), 65521 );
    end loop;
    t_last_chr := to_number( dbms_lob.substr( p_src, 2, dbms_lob.getlength( p_src ) - 1 ), '0XXX')
                - t_s1;
    if t_last_chr < 0
    then
      t_last_chr := t_last_chr + 65521;
    end if;
    dbms_lob.append( t_out, hextoraw( to_char( t_last_chr, 'fm0X' ) ) );
    if utl_compress.isopen( t_hdl )
    then
      utl_compress.lz_uncompress_close( t_hdl );
    end if;
    dbms_lob.freetemporary( t_tmp );
    return t_out;
  end;

Anton
sql script
package with zip and unzip

** Changelog:
** Date: 29-04-2012
** fixed bug for large uncompressed files, thanks Morten Braten
** Date: 21-03-2012
** Take CRC32, compressed length and uncompressed length from
** Central file header instead of Local file header
** Date: 17-02-2012
** Added more support for non-ascii filenames
** Date: 25-01-2012
** Added MIT-license
** Date: 31-01-2014
** file limit increased to 4GB

Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

The post Utl_compress, gzip and zlib appeared first on AMIS Oracle and Java Blog.

]]>
https://technology.amis.nl/2010/03/13/utl_compress-gzip-and-zlib/feed/ 12
Oracle RDBMS 11gR2 – Solving a Sudoku using Recursive Subquery Factoring https://technology.amis.nl/2009/10/13/oracle-rdbms-11gr2-solving-a-sudoku-using-recursive-subquery-factoring/ https://technology.amis.nl/2009/10/13/oracle-rdbms-11gr2-solving-a-sudoku-using-recursive-subquery-factoring/#comments Tue, 13 Oct 2009 10:11:27 +0000 http://technology.amis.nl/blog/?p=6404 Share this on .. Oracle Database 11g Release 2 introduces a new feature called Recursive Subquery Factoring. My collegue Lucas sees it as a substitute for Connect By based hierarchical querying, Oracle RDBMS 11gR2 – new style hierarchical querying using Recursive Subquery Factoring. When I first was thinking about a pratical use for this feature [...]

The post Oracle RDBMS 11gR2 – Solving a Sudoku using Recursive Subquery Factoring appeared first on AMIS Oracle and Java Blog.

]]>
Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

Oracle Database 11g Release 2 introduces a new feature called Recursive Subquery Factoring. My collegue Lucas sees it as a substitute for Connect By based hierarchical querying, Oracle RDBMS 11gR2 – new style hierarchical querying using Recursive Subquery Factoring. When I first was thinking about a pratical use for this feature I couldn’t come up with anything, but on second thought:: solving Sudokus!

Say you have a sudoku like:

 To solve this sudoku you first have to transforms this to a single string by appending all rows together:

"53  7    6  195    98    6 8   6   34  8 3  17   2   6 6    28    419  5    8  79"

Past this string into a Recursive Subquery, run it and you get a new string with your solved sudoku:

with x( s, ind ) as
( select sud, instr( sud, ' ' )
  from ( select '53  7    6  195    98    6 8   6   34  8 3  17   2   6 6    28    419  5    8  79' sud from dual )
  union all
  select substr( s, 1, ind - 1 ) || z || substr( s, ind + 1 )
       , instr( s, ' ', ind + 1 )
  from x
     , ( select to_char( rownum ) z
         from dual
         connect by rownum <= 9
       ) z
  where ind > 0
  and not exists ( select null
                   from ( select rownum lp
                          from dual
                          connect by rownum <= 9
                        )
                   where z = substr( s, trunc( ( ind - 1 ) / 9 ) * 9 + lp, 1 )
                   or    z = substr( s, mod( ind - 1, 9 ) - 8 + lp * 9, 1 )
                   or    z = substr( s, mod( trunc( ( ind - 1 ) / 3 ), 3 ) * 3
                                      + trunc( ( ind - 1 ) / 27 ) * 27 + lp
                                      + trunc( ( lp - 1 ) / 3 ) * 6
                                   , 1 )
                 )
)
select s
from x
where ind = 0
/

sqlplus

This string can be transformed back to a nice display of the solution

 

So with Recursive Subquery Factoring you can solve your sudokus in 1 statement wich does fit on your screen, not something like in Solving a Sudoku with 1 SQL-statement: the Model-clause

 

Anton

Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

The post Oracle RDBMS 11gR2 – Solving a Sudoku using Recursive Subquery Factoring appeared first on AMIS Oracle and Java Blog.

]]>
https://technology.amis.nl/2009/10/13/oracle-rdbms-11gr2-solving-a-sudoku-using-recursive-subquery-factoring/feed/ 45
Unwrapping 10G wrapped PL/SQL https://technology.amis.nl/2009/02/03/unwrapping-10g-wrapped-plsql/ https://technology.amis.nl/2009/02/03/unwrapping-10g-wrapped-plsql/#comments Mon, 02 Feb 2009 23:31:21 +0000 http://technology.amis.nl/blog/?p=4753 Share this on ..   Many people have tried to unwrap wrapped PL/SQL. Most people haven’t succeeded in doing it, but since Pete Finnegan’s presentation on the 2006 Black Hat conference, http://www.blackhat.com/presentations/bh-usa-06/BH-US-06-Finnigan.pdf, unwrapping PL/SQL on the older database versions, pre 10G, is possible. David Litchfield, in his book “The Oracle Hacker’s Handbook”, describes a method [...]

The post Unwrapping 10G wrapped PL/SQL appeared first on AMIS Oracle and Java Blog.

]]>
Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

 

Many people have tried to unwrap wrapped PL/SQL. Most people haven’t succeeded in doing it, but since Pete Finnegan’s presentation on the 2006 Black Hat conference, http://www.blackhat.com/presentations/bh-usa-06/BH-US-06-Finnigan.pdf, unwrapping PL/SQL on the older database versions, pre 10G, is possible. David Litchfield, in his book “The Oracle Hacker’s Handbook”, describes a method to unwrap code on a 10G database. According to the description it doesn’t seem too difficult to do:
 
“First, it is base64 decoded. Then, each byte is
resubstituted with a second corresponding substitution table. Last, the text is
decompressed, leaving the clear text of the PL/SQL.”
 
A year ago, after reading the book, I first tried to unwrap some code. Without success! But this year I was looking for a nice project to kill some time and I decided to give it another go. From Litchfield’s description it is clear that you need a substitution table. Litchfield didn’t reveal the table. I couldn’t find it with Google. Looking for it in the wrap utility it not allowed (and difficult too, see for instance this work from some Israeli researchers http://webcourse.cs.technion.ac.il/236349/Spring2009/ho/WCFiles/final_report.pdf ) So I had to find another way to get the table. I started with just looking at the output of some “simple” wrapped procedures. You can use wrap.exe to wrap the PL/SQL code in a file, but it is much easier to use dbms_ddl.wrap.
 
 
In this output we see a line with a000000 en 15 lines with abcd. These lines I have found in every wrapped code. The third line, in this example 367, has probably something to do with database version. On my 10.2.0.4 it’s always 367, on a10.2.0.1 it’s always 2e and with my 10.1.02.4 wrap.exe it’s always b2. The 19th line, indicates the type of the wrapped object . A procedure gives 7, a function 8 and a package body b.
The next line contains 2 hex numbers. The first is the length from the unwrapped text without the create + 1, the second is the length of the base64-encoded text. So looking at the output from dbms_ddl.wrap reveals some information, but even the small procedure from this example produces too much wrapped text for me to get a clue on a substitution table. But luckily it is possible to wrap even smaller pieces of code.
 
 
If we base64-decode this, skip the first 20 bytes (the size of a SHA1-hash, see Litchfields book) we have only 18 bytes left to decipher. For the base64-decoding we can use the package utl_encode. Oracle has also a package utl_compress, which can be used for Lempel-Ziv compression/decompression, but his time I used some Java in the database for compression/decompression (see attached file for the code). Now we have 2 options:
  • Find a substitution table, apply it, and decompress the result to get unwrapped PL/SQL code.
  • Compress PL/SQL code, and compare this to the base64-decoded output to FIND (a part of) the substitution table.
So let’s try the second option. The only problem is what to compress. I started with the same input I use for dbms_ddl.wrap.
 
 
Oops, the zipped code is one byte short. But remember the two hex numbers in the wrapped output. The first hex number was the length of the unwrapped code + 1. So I tried adding a newline character to the input of the compression. That gives 1 more byte for the zipped code, but it has another problem. In the output of the wrapped column we have 2 lines with 166. These lines should both have the same value in the zipped column. And that is not so. So I tried adding a space. With more success.
 
 
And this gives us 16 entries, from the 256, for the substation table. How easy can it be! Doing the same for “PACKAGE b” gives some more values, but “PACKAGE c” results again in 2 different “zipped values” for the same “wrapped values”. Adding a space to the input text wasn’t correct choice . After a few more trials I found out it has to the byte 0. So now we have a way to generate a part of the substitution table. Is it enough to create the complete substitution table? Calling this query in a PL/SQL loop with a lot of different names results again in errors. But this error has to do with the second byte. And for the people who don’t know the Zip-format by heart (http://www.gzip.org/zlib/rfc-zlib.html), that’s where the compression level is stored. So I changed the compression level to 9, and then I was able to generate a complete substitution table. And with this table unwrapping not that difficult at all. You can use it in a SQL-statement.
 
 
 
Or you can use it in a Java-program to unwrap the plb-files of all your lost sources.
 
The used code can be downloaded here used code
 
Anton
 
Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

The post Unwrapping 10G wrapped PL/SQL appeared first on AMIS Oracle and Java Blog.

]]>
https://technology.amis.nl/2009/02/03/unwrapping-10g-wrapped-plsql/feed/ 30
Puzzelen met SQL – Testdata Generatie https://technology.amis.nl/2007/10/22/puzzelen-met-sql-testdata-generatie/ https://technology.amis.nl/2007/10/22/puzzelen-met-sql-testdata-generatie/#comments Mon, 22 Oct 2007 11:51:06 +0000 http://technology.amis.nl/blog/?p=2418 Share this on .. Puzzelen met SQL – Testdata Generatie Anton Scheffer en Alex Nuijten   Dit artikel is de on-line tegenhanger van de rubriek Puzzelen met SQL die verschijnt in de Optimize, het vakblad voor Oracle ontwikkelaars in Nederland. Een van de grote en vervelende uitdagingen bij het maken van SQL Puzzels maar belangrijker [...]

The post Puzzelen met SQL – Testdata Generatie appeared first on AMIS Oracle and Java Blog.

]]>
Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

Puzzelen met SQL – Testdata Generatie

Anton Scheffer en Alex Nuijten

 

Dit artikel is de on-line tegenhanger van de rubriek Puzzelen met SQL die verschijnt in de Optimize, het vakblad voor Oracle ontwikkelaars in Nederland.

Een van de grote en vervelende uitdagingen bij het maken van SQL Puzzels maar belangrijker nog bij het bouwen van de demo-applicaties en vooral bij het testen van nieuw ontwikkelde applicaties is het samenstellen van een set van voorbeeld-, demo- of test-data. Eindeloos voornamen, achternamen, plaatsnamen en geboortedata invoeren is stomvervelend en als er ook nog eisen aan de kwaliteit worden gesteld – zoals een evenwichtige verdeling over een waardenbereik of een uniekheid van bepaalde combinaties van velden – wordt het helemaal een nachtmerrie.

 

Er zijn tools te koop die test-data generatie doen – misschien trouwens zijn er ook open source producten te krijgen. Maar in deze Puzzel gaan we zelf test-data genereren, met niets anders dan SQL tot onze beschikking.

Het einddoel van de opdracht is om de PERSONEN tabel 250 fictieve personen aan te maken die aan de volgende eisen voldoen:

– de combinatie voornaam, achternaam en straatnaam is uniek

– de huisnummers zijn willekeurig gekozen uit de reeks 1..125

– de geboortedata zijn willekeurig gekozen tussen 1-1-1930 en 31-12-2000

– de initialen bestaan uit een letter en zijn ook willekeurig gekozen

 

Er zijn drie hulptabellen beschikbaar die al gegevens bevatten: VOORNAMEN, ACHTERNAMEN en STRAATNAMEN, zie de source code

 

1.1        Uitdagingen

1) Schrijf een query die willekeurige waarden genereert uit de reeks van 1 tot 125

with input as
( select 250 aantal_waarden
       , 1 ondergrens
       , 125 bovengrens
  from dual
)
, random_numbers as 
( select round ( ondergrens + (bovengrens - ondergrens) * dbms_random.value ) value
  from all_source
     , input
  where rownum &lt;= aantal_waarden
)
select value
from random_numbers

Waarschijnlijk het eerste waar je aan denkt als je “willekeurig” leest, is DBMS_RANDOM. Dit built-in package is een random number generator.

De functie genaamd VALUE genereert een willekeurig nummer tussen de 0 en de 1 met een precisie van 38 getallen achter de comma. Omdat er zoveel getallen achter de comma kunnen staan maken we gebruik van de ROUND functie om het gegenereerde getal af te ronden.

Gebruik makend van Subquery Factoring (de WITH clause waar het statement mee begint) word staps gewijs een nummer generator gebouwd.

Het zou natuurlijk een stuk handiger zijn als Oracle zelf al zo’n soort functionaliteit zou bieden waarbij een willekeurig nummer wordt gegenereert en je als argument de onder- en bovengrens kan ingeven. Laat dat nou net het geval zijn. Er is van de VALUE functie binnen het DBMS_RANDOM een overloading beschikbaar die precies doet wat we willen.

Hier is de Specificatie:

-- get a random Oracle number x, low &lt;= x &lt; high
FUNCTION value (low IN NUMBER, high IN NUMBER) RETURN NUMBER;

 

De hele nummer generator zoals we die zojuist hebben gemaakt kan dan ook vervangen worden door:

select round (dbms_random.value (1, 125))
from all_source
where rownum &lt;= 250

Let erop dat het bovengrens niet inclusief is. Als je dus wilt dat 125 ook een mogelijk nummer is dan zal het laatste argument 126 moeten zijn, en zal de ROUND moeten wijzigen naar een TRUNC.

We gebruiken hier trouwens de tabel all_source als een dummy tabel om 250 records te selecteren, zie https://technology.amis.nl/blog/?p=392 voor een andere manier om records te genereren.

 

2) Maak een query die geboortedata genereert, tussen 1-1-1930 en 31-12-2000

 

Helaas heeft het DBMS_RANDOM geen functie om data te genereren. Hier zullen we dus zelf creatief moeten worden.

with input as
(select 250 aantal_waarden
      , 0 ondergrens
      , to_date ('31-12-2000','DD-MM-YYYY')
        -  to_date ('01-01-1930','DD-MM-YYYY') bovengrens
   from dual
)
, random_numbers as
( select round (dbms_random.value (ondergrens, bovengrens)) value
  from all_source
     , input
  where rownum &lt;= aantal_waarden
)
select to_date ('01-01-1930', 'dd-mm-yyyy') + value
  from random_numbers

 

Ook deze query maakt gebruikt van Subquery Factoring. Met behulp van Subquery Factoring kun je een naam geven aan een inline view. Hier hebben we als eerste een inline view gedefinieerd genaamd “Input”. Deze inline view bestaat slechts uit een enkele rij met daarin de boven- en ondergrenzen die we nodig hebben in het tweede deel van de query.

Het tweede stukje is waar de willekeurige datum bepaald wordt. Deze inline view, genaamd “Random_Numbers”, maakt gebruik van de eerder gedefinieerde Input inline view.

Als laatste stap de uiteindelijke query waar het allemaal om draait. Hier word een selectie gemaakt uit de inline view Random_Numbers.

Het is echter wel een voorwaarde dat de inline view die je definieerd ook daadwerkelijk wordt gebruikt in de query. Ofwel in een andere inline view (zoals Input die gebruikt word in Random_Numbers), ofwel in de hoofdquery (zoals Random_Numbers gebruikt wordt). Je kan alleen maar gebruik maken van een inline view die reeds eerder in het statement is gedeclareerd. Het omdraaien van de inline views is dus niet toegestaan:

with random_numbers as
( select ...
  from grenzen
     ,  ...
)
, grenzen as
( select ...
  from dual
)
...

Dit zal leiden tot een Oracle foutmelding:

ORA-32031: illegal reference of a query name in WITH clause

 

3) Maak een query die initialen genereert

 

Het is natuurlijk eenvoudig om voort te borduren op de generatoren zoals we die tot nu toe hebben geschreven, bijvoorbeeld zoiets als:

with input as
( select 250 aantal_waarden
       , 1 ondergrens
       , 26 bovengrens
  from dual
)
, random_numbers as
( select round (dbms_random.value (ondergrens, bovengrens)) value
  from all_source
     , input
  where rownum &lt;= aantal_waarden
)
select chr (64 + value)
from random_numbers

 

Maar het is nog een stuk eenvoudiger om gebruik te maken van iets wat Oracle reeds voor ons gemaakt heeft. In het DBMS_RANDOM package zit ook een functie genaamd STRING.

Deze functie geeft een willekeurige tekenreeks terug. Deze kan bestaan uit hoofdletters, kleine letters, een mix van hoofd- en kleine letters, letters en cijfers door elkaar of alleen karakters die printable zijn.

Hier is de functie specificatie:

-- get a random string
FUNCTION string (opt char, len NUMBER)

Met het eerste argument geef je aan wat voor soort string je terug wilt krijgen. Hieronder een lijstje van mogelijkheden:

  • U – Alleen hoofdletters
  • L – Alleen kleine letters
  • A – Hoofd- en kleine letters door elkaar
  • X – Alpha numerieke karakters
  • P – Printable karakters

Het maakt trouwens niet uit of dit argument een hoofd- of kleine letter is.

Het tweede argument is de lengte van de string die je terug wilt hebben.

Om willekeurige initialen te generen kun je dus volstaan met een query als:

select dbms_random.string ('u', 1)
from all_source
where rownum &lt;= 250

 

4) Schrijf een stukje SQL dat op basis van de hulptabellen VOORNAMEN, ACHTERNAMEN en STRAATNAMEN unieke combinaties maakt van Voornaam, Achternaam en Straatnaam met liefst een willekeurige verdeling van alle voorkomende waarden in de hulptabellen (dus liefst niet 250 keer Jan met wisselende combinaties van Achternaam en Straatnaam).

select voornaam
     , achternaam
     , straatnaam
from ( select *
       from voornamen
          , achternamen
          , straatnamen
       order by ORA_HASH (rownum)
     )
where rownum &lt;= 250

 

De basis van deze query is een Cartetisch product van de Voornamen, Achternamen en Straatnamen tabellen. Het meest opvallende uit deze query is de ORA_HASH functie.

ORA_HASH bepaald een hash waarde voor een gegeven argument. En waar is dat nu handig voor? Bijvoorbeeld om een willekeurig set gegevens te genereren, zoals in bovenstaande query.

Een andere mogelijkheid om een willekeurige set te krijgen is door de ORDER BY te vervangen door:

order by DBMS_RANDOM.VALUE

 

5) Laad de PERSONEN tabel met test-data volgens de voorwaarden die hierboven beschreven zijn.

Met behulp van de queries die we hiervoor hebben gemaakt, is het nu niet meer moeilijk om de PERSONEN tabel te vullen.

insert into personen( initialen, voornaam, achternaam, straatnaam, huisnummer, geboortedatum )
select dbms_random.string ('u', 1)
    , voornaam
    , achternaam
    , straatnaam
    , trunc( dbms_random.value (1, 126) )
    , to_date ('01-01-1930', 'dd-mm-yyyy')
    +  trunc( dbms_random.value( 0
                               , to_date ('01-01-2001', 'dd-mm-yyyy')
                               - to_date ('01-01-1930', 'dd-mm-yyyy')
                               )
        )
from ( select *
       from voornamen
          , achternamen
          , straatnamen
       order by ORA_HASH (rownum)
     )
where rownum &lt;= 250

En hier is nogmaals de zip met de source code en voorbeeld tabellen.

Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

The post Puzzelen met SQL – Testdata Generatie appeared first on AMIS Oracle and Java Blog.

]]>
https://technology.amis.nl/2007/10/22/puzzelen-met-sql-testdata-generatie/feed/ 2
Oracle 11G: describing a refcursor https://technology.amis.nl/2007/07/22/oracle-11g-describing-a-refcursor/ https://technology.amis.nl/2007/07/22/oracle-11g-describing-a-refcursor/#comments Sun, 22 Jul 2007 00:41:25 +0000 http://technology.amis.nl/blog/?p=2332 Share this on .. In Oracle 11G the supplied package DBMS_SQL is extended with two new procedures: to_cursor_number, which transfers a refcursor to a "dbms_sql cursor" to_refcursor, which transfers a "dbms_sql cursor" to a refcursor These can be used, together with dbms_sql.describe_columns to describe a refcursor. declare rc sys_refcursor; v varchar2(10); n number; c integer; [...]

The post Oracle 11G: describing a refcursor appeared first on AMIS Oracle and Java Blog.

]]>
Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

In Oracle 11G the supplied package DBMS_SQL is extended with two new procedures:

  • to_cursor_number, which transfers a refcursor to a "dbms_sql cursor"
  • to_refcursor, which transfers a "dbms_sql cursor" to a refcursor

These can be used, together with dbms_sql.describe_columns to describe a refcursor.

declare
  rc sys_refcursor;
  v varchar2(10);
  n number;
  c integer;
  cnt integer;
  dt dbms_sql.desc_tab3;
begin
  open rc for 'select dummy, cast( 4 as number(3,1)) from dual';
  c := dbms_sql.to_cursor_number( rc );
  dbms_sql.describe_columns3( c, cnt, dt );
  dbms_output.put_line( 'no. columns = ' || cnt );
  for i in 1 .. cnt
  loop
    dbms_output.put_line( dt(i).col_type );
    dbms_output.put_line( dt(i).col_name );
    dbms_output.put_line( dt(i).col_max_len );
    dbms_output.put_line( dt(i).col_precision );
    dbms_output.put_line( dt(i).col_scale );
  end loop;
  rc := dbms_sql.to_refcursor( c );
  fetch rc into v, n;
  close rc;
end;
/
no. columns = 2
1
DUMMY
1
0
0
2
CAST(4ASNUMBER(3,1))
22
3
1
PL/SQL procedure successfully completed.

Anton

Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

The post Oracle 11G: describing a refcursor appeared first on AMIS Oracle and Java Blog.

]]>
https://technology.amis.nl/2007/07/22/oracle-11g-describing-a-refcursor/feed/ 0
Oracle 11G: XMLQuery = eval https://technology.amis.nl/2007/07/14/oracle-11g-xmlquery-eval/ https://technology.amis.nl/2007/07/14/oracle-11g-xmlquery-eval/#comments Sat, 14 Jul 2007 18:39:33 +0000 http://technology.amis.nl/blog/?p=2302 Share this on .. A nice little trick on Oracle 11G is using XMLQuery as an eval function: SQL&gt;l   1  select substr( sys_connect_by_path( level, '*' ), 2 ) || ' = ' ||   2         XMLQuery( substr( sys_connect_by_path( level, '*' ), 2 ) RETURNING CONTENT).getnumberval() product   3       , substr( sys_connect_by_path( level, '+' ), [...]

The post Oracle 11G: XMLQuery = eval appeared first on AMIS Oracle and Java Blog.

]]>
Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

A nice little trick on Oracle 11G is using XMLQuery as an eval function:

SQL&gt;l
  1  select substr( sys_connect_by_path( level, '*' ), 2 ) || ' = ' ||
  2         XMLQuery( substr( sys_connect_by_path( level, '*' ), 2 ) RETURNING CONTENT).getnumberval() product
  3       , substr( sys_connect_by_path( level, '+' ), 2 ) || ' = ' ||
  4         XMLQuery( substr( sys_connect_by_path( level, '+' ), 2 ) RETURNING CONTENT).getnumberval() sum
  5  from dual
  6* connect by level &lt; 6
SQL&gt;/
PRODUCT                   SUM
------------------------- -------------------------
1 = 1                     1 = 1
1*2 = 2                   1+2 = 3
1*2*3 = 6                 1+2+3 = 6
1*2*3*4 = 24              1+2+3+4 = 10
1*2*3*4*5 = 120           1+2+3+4+5 = 15

Anton   Marco’s first performance  investigation:

== First Time ==

Execution Plan
———————————————————-
Plan hash value: 1236776825

—————————————————————————–
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
—————————————————————————–
| 0 | SELECT STATEMENT | | 1 | 2 (0)| 00:00:01 |
|* 1 | CONNECT BY WITHOUT FILTERING| | | | |
| 2 | FAST DUAL | | 1 | 2 (0)| 00:00:01 |
—————————————————————————–

Predicate Information (identified by operation id):
—————————————————

1 – filter(LEVEL<6)

Statistics
———————————————————-
2526 recursive calls
910 db block gets
1971 consistent gets
171 physical reads
16816 redo size
834 bytes sent via SQL*Net to client
407 bytes received via SQL*Net from client
4 SQL*Net roundtrips to/from client
191 sorts (memory)
0 sorts (disk)
5 rows processed

== Second Time ==

Execution Plan
———————————————————-
Plan hash value: 1236776825

—————————————————————————–
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
—————————————————————————–
| 0 | SELECT STATEMENT | | 1 | 2 (0)| 00:00:01 |
|* 1 | CONNECT BY WITHOUT FILTERING| | | | |
| 2 | FAST DUAL | | 1 | 2 (0)| 00:00:01 |
—————————————————————————–

Predicate Information (identified by operation id):
—————————————————

1 – filter(LEVEL<6)

Statistics
———————————————————-
71 recursive calls
900 db block gets
169 consistent gets
0 physical reads
0 redo size
834 bytes sent via SQL*Net to client
407 bytes received via SQL*Net from client
4 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
5 rows processed

 

Disclaimer

The information demonstrated and shared here is based on Oracle beta software. The following is intended to outline Oracle’s general product direction. It is intended for information purposes only, and may not be incorporated into any contract. It is not a commitment to deliver any material, code, or functionality, and should not be relied upon in making purchasing decisions. The development, release, and timing of any features or functionality described for Oracle’s products remains at the sole discretion of Oracle.

Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

The post Oracle 11G: XMLQuery = eval appeared first on AMIS Oracle and Java Blog.

]]>
https://technology.amis.nl/2007/07/14/oracle-11g-xmlquery-eval/feed/ 6