LOB (BLOB, CLOB) von entfernten Datenbanken lesen ...
Database Links haben, wie die meisten von euch wissen, so ihre Einschränkungen. So lassen sich BLOBS und CLOBs nicht so einfach über einen Database Link abfragen. Probiert man es, so kommt man nicht besonders weit ...
SQL> select content from dateien_tab@bloblink where file_name='04_XMLDB.pdf';
ERROR:
ORA-22992: cannot use LOB locators selected from remote tables
Eine Lösung kann allerdings (wieder einmal) Java in der Datenbank sein. Denn in dieser könnte man eine normale
JDBC-Verbindung zur entfernten Datenbank öffnen, den Blob selektieren, die Bytes per Java-Streaming lesen und
dann lokal (wieder als BLOB) zurückgeben.
create or replace and compile java source named "JAVA_RemoteBlob" as
import java.sql.*;
import oracle.sql.*;
import oracle.jdbc.*;
import java.io.*;
public class RemoteBlob {
private static Statement stmt = null;
private static ResultSet rs = null;
private static Connection remoteConn = null;
private static Connection localConn = null;
static {
try {
localConn = DriverManager.getConnection("jdbc:default:connection");
} catch (Exception e) {}
}
public static void prepareCursor (String sUser, String sPass, String sConn, String sSql) throws Exception {
String strUrl = "jdbc:oracle:thin:@" + sConn;
remoteConn = DriverManager.getConnection( strUrl, sUser, sPass );
stmt = remoteConn.createStatement();
rs = stmt.executeQuery(sSql);
}
public static void closeCursor () throws Exception {
rs.close();
stmt.close();
remoteConn.close();
}
public static Datum getBlob() throws Exception {
Datum localLOB = null;
Datum remoteLOB = null;
InputStream remoteBytes = null;
OutputStream localBytes = null;
Reader remoteChars = null;
Writer localChars = null;
byte[] bBuffer = null;
char[] cBuffer = null;
int iBytesRead = 0;
if (rs.next()) {
remoteLOB = ((OracleResultSet)rs).getOracleObject(1);
if (remoteLOB instanceof oracle.sql.BLOB) {
localLOB = BLOB.createTemporary(localConn, true, BLOB.DURATION_CALL);
remoteBytes = ((BLOB)remoteLOB).getBinaryStream(0L);
localBytes = ((BLOB)localLOB).getBinaryOutputStream(0L);
bBuffer = new byte[((BLOB)remoteLOB).getChunkSize()];
while ((iBytesRead = remoteBytes.read(bBuffer, 0, bBuffer.length)) != -1) {
localBytes.write(bBuffer, 0, iBytesRead);
}
localBytes.flush();
localBytes.close();
remoteBytes.close();
} else {
localLOB = CLOB.createTemporary(localConn, true, CLOB.DURATION_CALL);
remoteChars = ((CLOB)remoteLOB).getCharacterStream(0L);
localChars = ((CLOB)localLOB).getCharacterOutputStream(0L);
cBuffer = new char[((CLOB)remoteLOB).getChunkSize()];
while ((iBytesRead = remoteChars.read(cBuffer, 0, cBuffer.length)) != -1) {
localChars.write(cBuffer, 0, iBytesRead);
}
localChars.flush();
localChars.close();
remoteChars.close();
}
} else {
throw new Exception ("ResultSet Exhausted");
}
return localLOB;
}
}
/
sho err
create or replace package remote_lob is
procedure prepare_cursor(
p_user in varchar2, p_pass in varchar2, p_conn in varchar2, p_sql in varchar2
);
procedure close_cursor;
function get_blob return blob;
function get_clob return clob;
function get_blob_simple(
p_user in varchar2, p_pass in varchar2, p_conn in varchar2, p_sql in varchar2
) return blob;
function get_clob_simple(
p_user in varchar2, p_pass in varchar2, p_conn in varchar2, p_sql in varchar2
) return clob;
end remote_lob;
/
sho err
create or replace package body remote_lob is
procedure prepare_cursor(
p_user in varchar2, p_pass in varchar2, p_conn in varchar2, p_sql in varchar2
) as language java name 'RemoteBlob.prepareCursor(java.lang.String,java.lang.String,java.lang.String,java.lang.String)';
procedure close_cursor
as language java name 'RemoteBlob.closeCursor()';
function get_blob return blob
as language java name 'RemoteBlob.getBlob() return oracle.sql.BLOB';
function get_clob return clob
as language java name 'RemoteBlob.getBlob() return oracle.sql.CLOB';
function get_blob_simple(
p_user in varchar2, p_pass in varchar2, p_conn in varchar2, p_sql in varchar2
) return blob is
v_blob blob;
begin
prepare_cursor(p_user, p_pass, p_conn, p_sql);
v_blob := get_blob;
close_cursor;
return v_blob;
end get_blob_simple;
function get_clob_simple(
p_user in varchar2, p_pass in varchar2, p_conn in varchar2, p_sql in varchar2
) return clob is
v_lob clob;
begin
prepare_cursor(p_user, p_pass, p_conn, p_sql);
v_lob := get_clob;
close_cursor;
return v_lob;
end get_clob_simple;
end remote_lob;
/
sho err
Nun habt Ihr das Package REMOTE_LOB, welches euch erlaubt, BLOBs oder CLOBS aus entfernten Datenbanken
abzurufen. Der einzige Wermutstropfen ist, dass Username, Password und Connection-String hier nochmal
übergeben werden müssen: die Informationen eines Database Link kann man nicht nutzen, da die
das Passwort für die entfernte Datenbank nicht mehr aus den Dictionary Tabellen ausgelesen werden kann.
Vor dem Test braucht Ihr noch Java-Netzwerkprivilegien - ohne schlägt der JDBC-Connect fehl ...
ERROR in line 1: ORA-29532: "Java call terminated by uncaught Java exception: java.security.AccessControlException: the Permission (java.net.SocketPermission sccloud033 resolve) has not been granted to SCOTT. The PL/SQL to grant this is dbms_java.grant_permission( 'SCOTT', 'SYS:java.net.SocketPermission', 'sccloud033', 'resolve' ) ORA-06512: in "SCOTT.REMOTE_LOB", line 2 ORA-06512: in line 2
Also Privilegien vergeben (als DBA) ...
begin
dbms_java.grant_permission(
GRANTEE => 'SCOTT',
PERMISSION_TYPE => 'SYS:java.net.SocketPermission',
PERMISSION_NAME => '{remote-host}',
PERMISSION_ACTION => 'connect, resolve'
);
end;
/
.... und testen:
SQL> exec remote_lob.prepare_cursor('test','test','sccloud033:1521/orcl','select content from dateien_tab');
PL/SQL procedure successfully completed.
SQL> select dbms_lob.getlength(remote_lob.get_blob) from dual;
DBMS_LOB.GETLENGTH(REMOTE_LOB.GET_BLOB)
---------------------------------------
460800
1 row selected.
SQL> select dbms_lob.getlength(remote_lob.get_blob) from dual;
DBMS_LOB.GETLENGTH(REMOTE_LOB.GET_BLOB)
---------------------------------------
751616
1 row selected.
SQL> select remote_lob.get_blob from dual;
GET_BLOB
--------------------------------------------------------------------------------
D0CF11E0A1B11AE1000000000000000000000000000000003E000300FEFF09000600000000000000
00000000050000001502000000000000001000001702000001000000FEFFFFFF0000000025020000
1 Zeile wurde ausgewählt.
SQL> exec remote_lob.close_cursor;
PL/SQL procedure successfully completed.
Mit CLOBs funktioniert das analog. Die Funktionen GET_BLOB_SIMPLE und GET_CLOB_SIMPLE
dienen der "Bequemlichkeit" und machen alle drei Schritte auf einmal. Das Grundprinzip könnte sogar
verwendet werden, um LOBs aus einer anderen Datenbank auszulesen - schließlich kann man auch
den JDBC-Treiber einer anderen Datenbank laden. Das kommt vielleicht noch etwas später - bis jetzt
erstmal viel Spaß damit.
Keine Kommentare:
Kommentar veröffentlichen