Updating blob shows PL/SQL success but no content change - oracle

Attempting to update pictures in a blob column for multiple rows, I receive successful messages with no error and yet changes have not been rendered. I restarted Oracle service and machine and still nothing propagated. Yes, as shown I am committing and I have tried only one blob to no avail.
Is it the temporary dest_lob object? The readonly type? Do I have to release the temp blob?
Is it the appropriate schema not set? I am calling it with schema user.
Do previous blob content have to be NULL? The Hamlet.jpg attempt (ID = 101) maintains NULL in Picture blob column.
Is it a caching issue with Oracle? Do I have to refresh something like the tablespace file?
Is it the multiple BEGIN ... END calls?
Table
SQL > desc Characters;
Name Null? Type
----------------------------------------------------------------
ID NOT NULL NUMBER(5)
CHARACTER VARCHAR2(255 CHAR)
DESCRIPTION VARCHAR2(1000 CHAR)
SOURCE VARCHAR2(255 CHAR)
QUOTE VARCHAR2(1500 CHAR)
PICTURE BLOB
LINKIMAGE VARCHAR2(255 CHAR)
PL-SQL
CREATE OR REPLACE DIRECTORY MY_DIR AS '/path/to/pictures';
DECLARE
src_bfile BFILE := BFILENAME('MY_DIR', 'HenryCorwin.jpg');
dest_blob BLOB;
BEGIN
SELECT PICTURE into dest_blob FROM CHARACTERS WHERE ID = 44;
DBMS_LOB.OPEN(src_bfile, DBMS_LOB.LOB_READONLY);
DBMS_LOB.CREATETEMPORARY( dest_blob, TRUE);
DBMS_LOB.LoadFromFile( DEST_LOB => dest_blob,
SRC_LOB => src_bfile,
AMOUNT => DBMS_LOB.GETLENGTH(src_bfile) );
DBMS_LOB.CLOSE(src_bfile);
COMMIT;
END;
/
DECLARE
src_bfile BFILE := BFILENAME('MY_DIR', 'Hamlet.jpg');
dest_blob BLOB;
BEGIN
SELECT PICTURE into dest_blob FROM CHARACTERS WHERE ID = 101;
DBMS_LOB.OPEN(src_bfile, DBMS_LOB.LOB_READONLY);
DBMS_LOB.CREATETEMPORARY( dest_blob, TRUE);
DBMS_LOB.LoadFromFile( DEST_LOB => dest_blob,
SRC_LOB => src_bfile,
AMOUNT => DBMS_LOB.GETLENGTH(src_bfile) );
DBMS_LOB.CLOSE(src_bfile);
COMMIT;
END;
/
DECLARE
src_bfile BFILE := BFILENAME('MY_DIR', 'CGreen.jpg');
dest_blob BLOB;
BEGIN
SELECT PICTURE into dest_blob FROM CHARACTERS WHERE ID = 15;
DBMS_LOB.OPEN(src_bfile, DBMS_LOB.LOB_READONLY);
DBMS_LOB.CREATETEMPORARY( dest_blob, TRUE);
DBMS_LOB.LoadFromFile( DEST_LOB => dest_blob,
SRC_LOB => src_bfile,
AMOUNT => DBMS_LOB.GETLENGTH(src_bfile) );
DBMS_LOB.CLOSE(src_bfile);
COMMIT;
END;
/
DECLARE
src_bfile BFILE := BFILENAME('MY_DIR', 'SevenOfNine.jpg');
dest_blob BLOB;
BEGIN
SELECT PICTURE into dest_blob FROM CHARACTERS WHERE ID = 82;
DBMS_LOB.OPEN(src_bfile, DBMS_LOB.LOB_READONLY);
DBMS_LOB.CREATETEMPORARY( dest_blob, TRUE);
DBMS_LOB.LoadFromFile( DEST_LOB => dest_blob,
SRC_LOB => src_bfile,
AMOUNT => DBMS_LOB.GETLENGTH(src_bfile) );
DBMS_LOB.CLOSE(src_bfile);
COMMIT;
END;
/
sqlplus command line call (of above script)
SQL> #/path/to/script.sql
Console Output
Directory created.
PL/SQL procedure successfully completed.
PL/SQL procedure successfully completed.
PL/SQL procedure successfully completed.
PL/SQL procedure successfully completed.
SQL Blob data check
SELECT ID FROM Characters
WHERE DBMS_LOB.GETLENGTH(Picture) = 0 OR
DBMS_LOB.GETLENGTH(Picture) IS NULL;
Output
ID
----------
15
44
82
101
Environment
Ubuntu 16.04 LTS, 64-bit
Intel Celeron, 4-processor, 64-GB HD, 2-GB RAM
Oracle 11g Express:
SQL > select * from v$version
Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production
PL/SQL Release 11.2.0.2.0 - Production
CORE 11.2.0.2.0 Production
TNS for Linux: Version 11.2.0.2.0 - Production
NLSRTL Version 11.2.0.2.0 - Production

With help from this other forum thread, I found my issue. Instead of using the following LOB methods:
DBMS_LOB.OPEN(src_bfile, DBMS_LOB.LOB_READONLY);
Simply replace with the FILE version and remove the CREATETEMPORARY():
DBMS_LOB.FILEOPEN(src_bfile, DBMS_LOB.FILE_READONLY);
And in hindsight this makes somewhat sense since I am reading form a file object, src_bfile, and not an actual blob object.
With the following updated PL/SQL routine, blob columns successfully render.
CREATE OR REPLACE DIRECTORY MY_DIR AS '/path/to/pictures';
DECLARE
src_bfile BFILE := BFILENAME('MY_DIR', 'HenryCorwin.jpg');
dest_blob BLOB;
BEGIN
SELECT PICTURE into dest_blob FROM CHARACTERS WHERE ID = 44 FOR UPDATE;
DBMS_LOB.FILEOPEN(src_bfile, DBMS_LOB.FILE_READONLY);
DBMS_LOB.LoadFromFile( DEST_LOB => dest_blob,
SRC_LOB => src_bfile,
AMOUNT => DBMS_LOB.GETLENGTH(src_bfile) );
DBMS_LOB.FILECLOSE(src_bfile);
COMMIT;
END;
/
DECLARE
src_bfile BFILE := BFILENAME('MY_DIR', 'CGreen.jpg');
dest_blob BLOB;
BEGIN
SELECT PICTURE into dest_blob FROM CHARACTERS WHERE ID = 15 FOR UPDATE;
DBMS_LOB.FILEOPEN(src_bfile, DBMS_LOB.FILE_READONLY);
DBMS_LOB.LoadFromFile( DEST_LOB => dest_blob,
SRC_LOB => src_bfile,
AMOUNT => DBMS_LOB.GETLENGTH(src_bfile) );
DBMS_LOB.FILECLOSE(src_bfile);
COMMIT;
END;
/
DECLARE
src_bfile BFILE := BFILENAME('MY_DIR', 'SevenOfNine.jpg');
dest_blob BLOB;
BEGIN
SELECT PICTURE into dest_blob FROM CHARACTERS WHERE ID = 82 FOR UPDATE;
DBMS_LOB.FILEOPEN(src_bfile, DBMS_LOB.FILE_READONLY);
DBMS_LOB.CREATETEMPORARY( dest_blob, TRUE);
DBMS_LOB.LoadFromFile( DEST_LOB => dest_blob,
SRC_LOB => src_bfile,
AMOUNT => DBMS_LOB.GETLENGTH(src_bfile) );
DBMS_LOB.FILECLOSE(src_bfile);
COMMIT;
END;
/
Also, if blob column is NULL like my Hamlet.jpg attempt the above procedure will fail with
ORA-06502: PL/SQL: numeric or value error: invalid LOB locator
specified:
ORA-22275 ORA-06512: at "SYS.DBMS_LOB", line 928 ORA-06512:
at line 7
To resolve, update with EMPTY_BLOB() prior to updating the blob with the intended object.
UPDATE CHARACTERS SET Picture = EMPTY_BLOB() WHERE ID = 101;
DECLARE
src_bfile BFILE := BFILENAME('MY_DIR', 'Hamlet.jpg');
dest_blob BLOB;
BEGIN
SELECT PICTURE into dest_blob FROM CHARACTERS WHERE ID = 101 FOR UPDATE;
DBMS_LOB.FILEOPEN(src_bfile, DBMS_LOB.FILE_READONLY);
DBMS_LOB.LoadFromFile( DEST_LOB => dest_blob,
SRC_LOB => src_bfile,
AMOUNT => DBMS_LOB.GETLENGTH(src_bfile) );
DBMS_LOB.FILECLOSE(src_bfile);
COMMIT;
END;
/
Successful messages render and final query returns a sight to sore eyes:
SQL > SELECT ID FROM Characters WHERE DBMS_LOB.GETLENGTH(Picture) = 0 OR DBMS_LOB.GETLENGTH(Picture) IS NULL;
no rows selected
SQL > SQL> SELECT DBMS_LOB.GETLENGTH(Picture) FROM Characters WHERE ID IN (15, 44, 82, 101);
DBMS_LOB.GETLENGTH(PICTURE)
---------------------------
365256
412300
381586
404241

I'll suggest you do an dbms_lob.erase, which seems to be necessary:
SELECT PICTURE INTO dest_blob FROM CHARACTERS WHERE ID = 15
FOR UPDATE;
dest_length := DBMS_LOB.GETLENGTH(dest_blob);
IF dest_length <> 0 THEN
DBMS_LOB.ERASE(dest_blob,dest_length,1);
END IF;
-- then do as you wrote:
DBMS_LOB.OPEN(src_bfile, DBMS_LOB.LOB_READONLY);
DBMS_LOB.CREATETEMPORARY( dest_blob, TRUE);
DBMS_LOB.LoadFromFile( DEST_LOB => dest_blob,
SRC_LOB => src_bfile,
AMOUNT => DBMS_LOB.GETLENGTH(src_bfile) );
DBMS_LOB.CLOSE(src_bfile);
COMMIT;
Hope this helps (stolen from here)

Related

Using APEX_DATA_EXPORT to store PDF files in DB

I am trying to figure out how to use APEX_DATA_EXPORT API to store PDF file in database table. I created a table with one column as a blob.
Has anyone tried to use APEX_DATA_EXPORT to store data in database?
DECLARE
l_context apex_exec.t_context;
l_export apex_data_export.t_export;
BEGIN
apex_session.create_session (
p_app_id => 130408,
p_page_id => 1,
p_username => 'EXAMPLE USER' );
l_context := apex_exec.open_query_context(
p_location => apex_exec.c_location_local_db,
p_sql_query => 'select * from emp' );
l_export := apex_data_export.export (
p_context => l_context,
p_format => nvl(:format, apex_data_export.c_format_pdf),
p_file_name => 'employees' );
apex_exec.close( l_context );
apex_data_export.download(
p_export => l_export,
--p_content_disposition => apex_data_export.c_inline,
p_stop_apex_engine => false);
insert into pdf_test4 (pdf) values l_export;
EXCEPTION
when others THEN
apex_exec.close( l_context );
raise;
END;
I managed to get it working for Excel so I guess it would work the same for pdf (change p_format)
DECLARE
v_sql varchar2(32000) := 'select * from my_data_table';
v_sql_trimmed varchar2(32000);
l_query_output CLOB;
p_file_id number;
l_file_name varchar2(200) := 'My File - '||sysdate;
p_status varchar2(100);
p_tag_id number;
l_context apex_exec.t_context;
l_export apex_data_export.t_export;
BEGIN
begin
l_context := apex_exec.open_query_context(
p_location => apex_exec.c_location_local_db,
p_sql_query => v_sql );
l_export := apex_data_export.export (
p_context => l_context,
p_format => apex_data_export.c_format_xlsx,
p_file_name => l_file_name );
apex_exec.close( l_context );
insert into my_files_table columns (file_blob,filename,file_mimetype,TAG,LOG,TAG_ID) values (l_export.content_blob , l_export.file_name,l_export.mime_type,'Latest','Y',1);
EXCEPTION
when others THEN
apex_exec.close( l_context );
raise;
END;
end;

Can't Read/Open Word Document After Conversion from BLOB to CLOB (Replace a URL string) then convert back to BLOB using Oracle APEX

I have some code that will take a word document BLOB convert to a CLOB find and replace a URL and then convert back to a BLOB using APEX_ZIP.
The Procedure executes fine but the document will not open as it appears to be corrupted when I do a replace on some text when the BLOB has converted to its CLOB format and then back to BLOB.
Where might I be going wrong, when I do dbms_output I can't see any discrepancies asthe string has replaced fine it seems.
DECLARE
l_old_file BLOB;
l_new_file BLOB;
l_files apex_zip.t_files;
l_document BLOB;
l_clob CLOB;
l_dest_offsset INTEGER;
l_src_offsset INTEGER;
l_lang_context INTEGER := DBMS_LOB.default_lang_ctx;
l_warning INTEGER;
BEGIN
SELECT file_data
INTO l_old_file
FROM documents
WHERE doc_no = '123';
l_files := apex_zip.get_files (l_old_file);
FOR i IN l_files.FIRST .. l_files.LAST
LOOP
l_document := apex_zip.get_file_content (l_old_file, l_files (i));
IF l_files (i) = 'word/document.xml'
THEN
-- if the file name is word/document.xml then make the changes to it
DBMS_LOB.createTemporary (lob_loc => l_clob, cache => FALSE);
l_dest_offsset := 1;
l_src_offsset := 1;
DBMS_LOB.converttoclob (dest_lob => l_clob,
src_blob => l_document,
amount => DBMS_LOB.lobmaxsize,
dest_offset => l_dest_offsset,
src_offset => l_src_offsset,
blob_csid => DBMS_LOB.default_csid,
lang_context => l_lang_context,
warning => l_warning);
--------------------
-- This is where you would do any replacements
--------------------
l_clob := REPLACE (l_clob, 'www.google.co.uk', 'www.google.com');
--------------------
l_dest_offsset := 1;
l_src_offsset := 1;
DBMS_LOB.CONVERTTOBLOB (dest_lob => l_document,
src_clob => l_clob,
amount => DBMS_LOB.lobmaxsize,
dest_offset => l_dest_offsset,
src_offset => l_src_offsset,
blob_csid => DBMS_LOB.default_csid,
lang_context => l_lang_context,
warning => l_warning);
END IF;
apex_zip.add_file (l_new_file, l_files (i), l_document);
END LOOP;
apex_zip.finish (l_new_file);
update documents set file_data = l_new_file where doc_no = '123';
END;
Many Thanks,

BLOB loading Oracle - ORA-22288

Purpose: I want to load blob data types into an oracle table. (I don't want to use sqlldr).
Error: ORA-22288: file or LOB operation FILEOPEN failed
Src code:
CREATE OR REPLACE DIRECTORY BLOB_DIR AS 'C:\';
CREATE TABLE tab1 (
id NUMBER,
blob_data BLOB
);
DECLARE
l_bfile BFILE;
l_blob BLOB;
l_dest_offset INTEGER := 1;
l_src_offset INTEGER := 1;
BEGIN
INSERT INTO tab1 (id, blob_data)
VALUES (1, empty_blob())
RETURN blob_data INTO l_blob;
l_bfile := BFILENAME('BLOB_DIR', 'MyImage.gif');
DBMS_LOB.fileopen(l_bfile, DBMS_LOB.file_readonly);
-- loadfromfile deprecated.
-- DBMS_LOB.loadfromfile(l_blob, l_bfile, DBMS_LOB.getlength(l_bfile));
DBMS_LOB.loadblobfromfile (
dest_lob => l_blob,
src_bfile => l_bfile,
amount => DBMS_LOB.lobmaxsize,
dest_offset => l_dest_offset,
src_offset => l_src_offset);
DBMS_LOB.fileclose(l_bfile);
COMMIT;
END;
Additional Info: I have a file with the following filepath - C:\MyImage.gif that I am trying to load. (Ideally I want to load a bunch but I am starting with one)
I'm using the following guide but I keep running into problems (https://oracle-base.com/articles/8i/import-blob#:~:text=CREATE%20OR%20REPLACE%20DIRECTORY%20BLOB_DIR,insert%20it%20into%20the%20table.)

advanced queuing for table replication to other database

So, I am trying to replicate the data in tables to another database using Advanced Queuing. I created a table on both databases:
create table test
(
id number(10) primary key,
text varchar2(100)
);
then I created the queue
create type table_repli_payload_type AS OBJECT
( rowid_record varchar2(100)
, tabelle VARCHAR2(255)
, schema VARCHAR2(50)
);
begin
SYS.DBMS_AQADM.create_queue_table(queue_table => 'table_repli_queue_table',
queue_payload_type => 'table_repli_payload_type',
multiple_consumers => TRUE);
SYS.DBMS_AQADM.CREATE_QUEUE (
queue_name => 'table_repli_queue',
queue_table => 'table_repli_queue_table'
);
SYS.DBMS_AQADM.START_QUEUE (
queue_name => 'table_repli_queue'
);
end;
wrote a procedure for the replication
create or replace procedure table_repli_callback(
context RAW,
reginfo SYS.AQ$_REG_INFO,
descr SYS.AQ$_DESCRIPTOR,
payload RAW,
payloadl NUMBER
) AS
r_dequeue_options DBMS_AQ.DEQUEUE_OPTIONS_T;
r_message_properties DBMS_AQ.MESSAGE_PROPERTIES_T;
v_message_handle RAW(16);
o_payload table_repli_payload_type;
v_error varchar2(4000);
BEGIN
insert into log_table values(sysdate, 'start table_repli_callback');
r_dequeue_options.msgid := descr.msg_id;
r_dequeue_options.consumer_name := descr.consumer_name;
DBMS_AQ.DEQUEUE(
queue_name => descr.queue_name,
dequeue_options => r_dequeue_options,
message_properties => r_message_properties,
payload => o_payload,
msgid => v_message_handle
);
insert into log_table values(sysdate, 'ROWID: '||o_payload.rowid_record);
merge into test#test_link a
using (select * from test where rowid=o_payload.rowid_record) b on (a.id=b.id)
when matched then
update set text=b.text
when not matched then
insert values(b.id, b.text);
COMMIT;
exception
when others then
v_error:=sqlerrm;
insert into log_table values(sysdate, 'ERROR: '||v_error);
commit;
END;
/
and subscribed it
BEGIN
DBMS_AQADM.ADD_SUBSCRIBER (
queue_name => 'table_repli_queue',
subscriber => SYS.AQ$_AGENT(
'table_repli_queue_subscriber',
NULL,
NULL )
);
DBMS_AQ.REGISTER (
SYS.AQ$_REG_INFO_LIST(
SYS.AQ$_REG_INFO(
'table_repli_queue:table_repli_queue_subscriber',
DBMS_AQ.NAMESPACE_AQ,
'plsql://table_repli_callback',
HEXTORAW('FF')
)
),
1
);
END;
/
I played around with inserting/updating data in the TEST-table and than executing(with changing ids) this Code
DECLARE
r_enqueue_options DBMS_AQ.ENQUEUE_OPTIONS_T;
r_message_properties DBMS_AQ.MESSAGE_PROPERTIES_T;
v_message_handle RAW(16);
o_payload table_repli_payload_type;
v_rowid_record varchar2(100);
BEGIN
select rowid
into v_rowid_record
from test
where id=2;
o_payload := table_repli_payload_type(
v_rowid_record, '', ''
);
DBMS_AQ.ENQUEUE(
queue_name => 'table_repli_queue',
enqueue_options => r_enqueue_options,
message_properties => r_message_properties,
payload => o_payload,
msgid => v_message_handle
);
COMMIT;
END;
/
It's pretty random if it is working. He always seems to write something into TABLE_REPLI_QUEUE_TABLE, but when it is gone, about half the time there doesn't appear anything in LOG_TABLE and the data in the second database hasn't changed.
The error was a strange behavior in TOAD.
I sometimes write test scripts, with ddl, dml, selects, pl/sql-blocks in it and execute them by placing my cursor in a part of the desired command and press shift+F9. It seems like my TOAD just didn't execute the PL/SQL-block although it told me, it did.
I put the PL/SQL-block in another tab and just hit F9 and it worked fine, every time.

Selecting an oracle xmltype table field into an xmltype variable results in a null object

This has been bugging me for a while. I'm using oracle streams to write messages to an oracle AQ queue as part of a stored procedure. Below is the stored proc with relevent sections shown:
CREATE OR REPLACE PROCEDURE ESBEVENT.esb_dml_handler(in_any IN ANYDATA) IS
l_enqueue_options dbms_aq.enqueue_options_t;
l_message_properties dbms_aq.message_properties_t;
l_message sys.aq$_jms_text_message;
l_msgid raw(16);
l_xmlmsg SYS.XMLTYPE;
err_num NUMBER;
err_msg VARCHAR2(100);
BEGIN
BEGIN
l_message := sys.aq$_jms_text_message.construct;
l_xmlmsg := DBMS_STREAMS.CONVERT_LCR_TO_XML(in_any);
l_message.set_text(l_xmlmsg.getClobVal());
dbms_aq.enqueue
( queue_name => 'esbevent.esb_jms_queue'
, enqueue_options => l_enqueue_options
, message_properties => l_message_properties
, payload => l_message
, msgid => l_msgid
);
COMMIT;
err_num := '';
err_msg := 'Message Queued Successfully';
EXCEPTION
WHEN OTHERS THEN
err_num := SQLCODE;
err_msg := SUBSTR(SQLERRM, 1, 1000);
INSERT INTO esbevent.esb_dml_handler_error_t VALUES (SYSDATE, err_num||' - '||err_msg );
END;
INSERT INTO esbevent.esb_jms_msg_memento_t VALUES (esb_jms_msg_memento_seq.nextval, SYSDATE, l_xmlmsg, err_num||' - '||err_msg );
COMMIT;
END;
Following the enqueue I store the message that was sent as an XMLTYPE in the table esb_jms_msg_memento_t. This is to enable resends if we have issues in subsequent processing. I'm now trying to write a block that will extract the XMLTYPE field data and write it again to the AQ. This is currently as follows:
DECLARE
l_enqueue_options DBMS_AQ.ENQUEUE_OPTIONS_T;
l_message_properties DBMS_AQ.MESSAGE_PROPERTIES_T;
l_message SYS.aq$_jms_text_message;
l_msgid RAW (16);
l_xmlmsg XMLTYPE;
BEGIN
SELECT msg_payload
INTO l_xmlmsg
FROM esb_jms_msg_memento_t
WHERE UNIQUE_ID = '815929';
l_message.set_text (l_xmlmsg.getClobVal ()); --null self error here
DBMS_AQ.enqueue (queue_name => 'esbevent.esb_jms_queue',
enqueue_options => l_enqueue_options,
message_properties => l_message_properties,
payload => l_message,
msgid => l_msgid);
COMMIT;
END;
When i run this I get the error:
ORA-30625: method dispatch on NULL SELF argument is disallowed
ORA-06512: at line 22
It appears I can insert and XMLTYPE variable to an XMLTYPE field but not the other way around???
As soon as I post I solve it...
forgot to construct the l_message:
l_message := sys.aq$_jms_text_message.construct;
doh!

Resources