Send Email procedure to multiple receivers - oracle

May I know my procedure any problem? I try to run the procedure and it works only for "CC" list receivers but "TO" list receivers cannot to receive the email. Any body can help me out will be great appreciate! Please provide me modified code.
create or replace PROCEDURE check_ap_reject_list (errbuf VARCHAR2, retcode NUMBER) IS
CURSOR c1 IS
SELECT distinct b.NAME AP_ORG, a.ORG_ID ORG_ID, a.source source,
a.INVOICE_ID INVOICE_ID,reject_lookup_code Reject_Reason,
a.INVOICE_NUM Invoice_Num, a.INVOICE_DATE Invoice_Date, a.GL_DATE GL_Date,
a.VENDOR_NAME Vendor_Name, a.VENDOR_SITE_CODE Vendor_Site_Code,
a.INVOICE_AMOUNT INVOICE_AMOUNT, a.INVOICE_CURRENCY_CODE INVOICE_CURRENCY_CODE, a.EXCHANGE_RATE EXCHANGE_RATE
FROM apps.ap_interface_rejections,
apps.ap_invoices_interface a,
apps.hr_all_organization_units b,
apps.ap_invoice_lines_interface d
WHERE (((parent_id = a.invoice_id AND parent_table = 'AP_INVOICES_INTERFACE') OR (parent_id = d.invoice_line_id AND parent_table = 'AP_INVOICE_LINES_INTERFACE')))
AND a.INVOICE_ID = d.INVOICE_ID
AND a.ORG_ID = b.ORGANIZATION_ID
AND a.source = 'RMS'
;
r1 c1%rowtype;
c utl_smtp.connection;
v_instance varchar2(9);
x_email_to varchar2(4000);
x_email_cc varchar2(4000);
v_temp_str varchar2(4000);
v_temp number;
v_r1_row_num number;
v_check number;
PROCEDURE send_data(p_data in varchar2) as
BEGIN
utl_smtp.write_raw_data(c, utl_raw.cast_to_raw(p_data || utl_tcp.crlf));
--fnd_file.put_line(fnd_file.output, p_data);
END;
BEGIN
SELECT count(distinct a.INVOICE_NUM)
into v_check
FROM apps.ap_interface_rejections, apps.ap_invoices_interface a, apps.hr_all_organization_units b, apps.ap_invoice_lines_interface d
WHERE (((parent_id = a.invoice_id AND parent_table = 'AP_INVOICES_INTERFACE') OR (parent_id = d.invoice_line_id AND parent_table = 'AP_INVOICE_LINES_INTERFACE')))
AND a.INVOICE_ID = d.INVOICE_ID
AND a.ORG_ID = b.ORGANIZATION_ID
AND a.source = 'RMS';
IF v_check > 0 THEN
select name into v_instance from V$DATABASE;
x_email_to := 'danielliu#abc.com, vincentyuan#abc.com, christinewong#abc.com';
x_email_cc := 'andycheng#abc.com, tonyfok#abc.com';
c := utl_smtp.open_connection('LCEX7',25);
--utl_smtp.open_connection('smtp.office365.com',587);
--utl_smtp.helo(c, 'LCEX7');
utl_smtp.helo(c, 'LCEX7');
utl_smtp.mail(c, 'lc_ofin#abc.com');
-- utl_smtp.rcpt( c, x_email_to);
-- utl_smtp.rcpt( c, x_email_cc);
v_temp_str := replace(x_email_to, ',', ';');
v_temp_str := replace(x_email_cc, ',', ';');
while length(v_temp_str) > 0 loop
v_temp := instr(v_temp_str, ';', 1, 1);
if v_temp > 0 then
utl_smtp.rcpt(c, trim(substr(v_temp_str, 1, v_temp - 1)));
v_temp_str := substr(v_temp_str, v_temp + 1, length(v_temp_str));
else
utl_smtp.rcpt(c, trim(v_temp_str));
v_temp_str := null;
end if;
end loop;
utl_smtp.open_data(c);
utl_smtp.write_data(c,'From: "Oracle Fin (' || v_instance ||') " <lc_ofin#abc.com>' || utl_tcp.CRLF);
utl_smtp.write_data(c, 'To: ' || x_email_to || utl_tcp.CRLF);
utl_smtp.write_data(c, 'Cc: ' || x_email_cc || utl_tcp.CRLF);
utl_smtp.write_data(c, 'Subject: AP invoice reject list ' || to_char(sysdate,'MM/dd/yyyy HH:mm:ss') || utl_tcp.CRLF);
utl_smtp.write_data(c, 'MIME-Version:' || '1.0' || utl_tcp.CRLF);
utl_smtp.write_data(c,'Content-Type: ' || 'text/html; charset=utf-8' ||utl_tcp.CRLF);
utl_smtp.write_data(c, 'Content-Transfer-Encoding: ' || '"8Bit"' || utl_tcp.CRLF);
send_data('<html><body>');
send_data('<p>Dear All,</p>');
send_data('<p>Please find the enclosed AP reject list and advise. Thanks!</p>');
send_data('<table border=''0'' width=''100%'' cellpadding=''1'' cellspacing=''1''>');
send_data('<tr style=''font-size:75%;font-weight:bold;''>');
send_data('<td>AP ORG</td>
<td>ORG ID</td>
<td>Source</td>
<td>INVOICE ID</td>
<td>Reject Reason</td>
<td>Invoice Num</td>
<td>Invoice Date</td>
<td>GL Date</td>
<td>Vendor Name</td>
<td>Vendor Site Code</td>
<td>INVOICE AMOUNT</td>
<td>INVOICE CURRENCY CODE</td>
<td>EXCHANGE RATE</td>');
v_r1_row_num := 0;
open c1;
loop
fetch c1
into r1;
exit when c1%notfound;
if (mod(v_r1_row_num,2)=0) then
send_data('<tr style=''font-size:75%;background-color:#E0EBFF;''>');
else
send_data('<tr style=''font-size:75%;''>');
end if;
send_data('<td>' || r1.AP_ORG || '</td>
<td>' || r1.ORG_ID || '</td>
<td>' || r1.source || '</td>
<td>' || r1.INVOICE_ID || '</td>
<td>' || r1.Reject_Reason || '</td>
<td>' || r1.Invoice_Num || '</td>
<td>' || r1.Invoice_Date || '</td>
<td>' || r1.GL_Date || '</td>
<td>' || r1.Vendor_Name || '</td>
<td>' || r1.Vendor_Site_Code|| '</td>
<td>' || r1.INVOICE_AMOUNT || '</td>
<td>' || r1.INVOICE_CURRENCY_CODE || '</td>
<td>' || r1.EXCHANGE_RATE || '</td></tr>');
v_r1_row_num := v_r1_row_num + 1;
END LOOP;
CLOSE c1;
send_data('</table>');
send_data('<p>Best Regards,</p>');
send_data('<p>Technology Department</p>');
send_data('</body></html>');
utl_smtp.close_data(c);
utl_smtp.quit(c);
end if ;
END check_ap_reject_list;

You need a RCPT for each email recipient (whether CC or TO), and then a line with "To" and "Cc" where appropriatein the email data. So some pseudo code would be along the lines of:
declare
x_email_to sys.odcivarchar2list := sys.odcivarchar2list('danielliu#abc.com, vincentyuan#abc.com, christinewong#abc.com');
x_email_cc sys.odcivarchar2list := sys.odcivarchar2list('andycheng#abc.com, tonyfok#abc.com');
begin
...
... normal email initialisation (open,helo,etc)
...
for i in 1 .. x_email_to.count loop
utl_smtp.rcpt(l_conn, x_email_to(i));
end loop;
for i in 1 .. x_email_cc.count loop
utl_smtp.rcpt(l_conn, x_email_cc(i));
end loop;
for i in 1 .. x_email_to.count loop
utl_smtp.write_data(c, 'To: ' || x_email_to(i) || utl_tcp.CRLF);
end loop;
for i in 1 .. x_email_cc.count loop
utl_smtp.write_data(c, 'Cc: ' || x_email_cc(i) || utl_tcp.CRLF);
end loop;
...
... rest of email
...
end;
I'm using an array so it easy to loop through each email address. In your case, you can just parse the string looking for each comment, but the concept is the same.
One RCPT per recipient
One write_data per CC and TO

Connor's answer is spot on if you must use the utl_smtp package.
Unless you have a need to directly operate against the SMTP protocol, however, that's a relatively cumbersome way to send an email. In older versions of Oracle, that was the way you had to send emails. Even back in the 10g days, Oracle had the utl_mail.send procedure that let you pass in comma-separated lists of recipients for the to, cc, and bcc lists along with an HTML message
utl_mail.send( sender => 'reply-to#your-domain.com',
recipients => x_email_to,
cc => x_email_cc,
subject => 'Subject: AP invoice reject list ' ||
to_char(sysdate,'MM/dd/yyyy HH:mm:ss'),
message => x_html_document,
mime_type => 'text/html' );
No need to worry about appending all the CRLF's manually, no need to make dozens of calls to write_data and/or write_raw_data. Just build the HTML document you want to send in a local variable and send it. Oracle already built the infrastructure for 99% of use cases.
If you happen to have APEX installed in your database (even if you don't have an APEX application), you can also use the apex_mail package which has a similar send package. That version simplifies the process if you want to send a HTML and a plain text version of the same email so that users whose email clients don't support HTML can view it. And since apex_mail enqueues messages to be sent rather than talking directly to the SMTP server, apex_mail calls are transactional so they'll roll back if the parent transaction fails which is nice to avoid spamming internal users if this is a process that can be retried in the event of an unexpected error.

Related

PL/SQL UTL_HTTP send multiple raws

I'm trying to send with webservice POST method a dozens of records to other side but problem is that other side get only one raw of data.
I put cursor and in begin section open cursor and make loop.
I think the problem is somewhere in part UTL_HTTP.read_raw. I have to add something additional or modify function.
Where is the catch, where I'm wrong?
create or replace FUNCTION "TST" return clob is
l_http_request UTL_HTTP.req;
l_http_response UTL_HTTP.resp;
l_buffer_size NUMBER(10) := 20000;
l_line_size NUMBER(10) := 50;
l_lines_count NUMBER(10) := 20;
l_string_request VARCHAR2(32767);
l_line VARCHAR2(128);
l_substring_msg VARCHAR2(20000);
l_raw_data RAW(20000);
l_clob_response CLOB ;
v_clob clob;
url varchar2(200) := 'http://tst';
cursor c_get_prihod is
select IDORIG , IDSUSTAVA , DATUM , SIFAGENCIJE , SIFDRZAVE ,
SIFOSUSL , IZNOSNETO , IZNOSBRUTO , IZNOSPOREZA , SIFHOTELA
from HRS_LASERLINE_PRIHOD
where datum = pms_p.business_date
and sifhotela = pms_p.resort
;
begin
for a in c_get_prihod loop
l_string_request:=
'{
IdOrig:"'||a.IDORIG||'",
IdSustava:"'||a.IDSUSTAVA||'",
Datum:"'||a.DATUM||'",
SifAgencije:"'||a.SIFAGENCIJE||'",
SifDrzave:"'||a.SIFDRZAVE||'",
SifOsUsl:"'||a.SIFOSUSL||'",
IznosNeto:"'||a.IZNOSNETO||'",
IznosBruto:"'||a.IZNOSBRUTO||'",
IznosPoreza:"'||a.IZNOSPOREZA||'",
SifHotela:"'||a.SIFHOTELA||'",
}}'
;
end loop;
l_http_request := UTL_HTTP.begin_request(url , method => 'POST', http_version => 'HTTP/1.1');
utl_http.set_header(l_http_request, 'user-agent', 'mozilla/4.0');
utl_http.set_header(l_http_request, 'content-type', 'application/json');
utl_http.set_header(l_http_request, 'Content-Length', length(l_string_request));
BEGIN
<<request_loop>>
FOR i IN 0..CEIL(LENGTH(l_string_request) / l_buffer_size) - 1 LOOP
l_substring_msg := SUBSTR(l_string_request, i * l_buffer_size + 1, l_buffer_size);
BEGIN
l_raw_data := utl_raw.cast_to_raw(l_substring_msg);
UTL_HTTP.write_raw(r => l_http_request, data => l_raw_data);
EXCEPTION
WHEN NO_DATA_FOUND THEN
EXIT request_loop;
END;
END LOOP request_loop;
l_http_response := UTL_HTTP.get_response(l_http_request);
BEGIN
LOOP
UTL_HTTP.read_raw(l_http_response, l_raw_data, l_buffer_size);
l_clob_response := l_clob_response || UTL_RAW.cast_to_varchar2(l_raw_data);
END LOOP response_loop;
EXCEPTION
WHEN UTL_HTTP.end_of_body THEN
UTL_HTTP.end_response(l_http_response);
END;
<<print_response>>
FOR i IN 0..CEIL(LENGTH(l_clob_response) / l_line_size) - 1
LOOP
l_line := SUBSTR(l_clob_response, i * l_line_size + 1, l_line_size);
EXIT WHEN i > l_lines_count - 1;
END LOOP print_response;
v_clob:= l_clob_response;
return v_clob ;
exception
when utl_http.end_of_body then
utl_http.end_response(l_http_response);
end;
end TST;
Your cursor query might find dozens of rows, but you're doing this:
for a in c_get_prihod loop
l_string_request:= ... ;
end loop;
Each time around that loop you're replacing the entire contents of l_string_request, so however many rows you get and how many times you go around the loop, you'll always end up with that containing only the last row returned by that query. So you only send that single, final, record to the web service.
Assuming the receiving service is expecting an array of records, you might want something like:
begin
l_string_request := '[ ';
for a in c_get_prihod loop
if a.FLAG > 1 then
l_string_request := l_string_request || ', ';
end if;
l_string_request := l_string_request || '{ ';
l_string_request := l_string_request || '"IdOrig": "' || a.IDORIG || '", ';
l_string_request := l_string_request || '"IdSustava": "' || a.IDSUSTAVA ||' ", ';
l_string_request := l_string_request || '"Datum": "' || a.DATUM || '", ';
l_string_request := l_string_request || '"SifAgencije": "' || a.SIFAGENCIJE || '", ';
l_string_request := l_string_request || '"SifDrzave": "' || a.SIFDRZAVE || '", ';
l_string_request := l_string_request || '"SifOsUsl": "' || a.SIFOSUSL || '", ';
l_string_request := l_string_request || '"IznosNeto": "' || a.IZNOSNETO || '", ';
l_string_request := l_string_request || '"IznosBruto": "' || a.IZNOSBRUTO || '", ';
l_string_request := l_string_request || '"IznosPoreza": "' || a.IZNOSPOREZA || '", ';
l_string_request := l_string_request || '"SifHotela": "' || a.SIFHOTELA || '"';
l_string_request := l_string_request || ' }';
end loop;
l_string_request := l_string_request || ' ]';
dbms_output.put_line(l_string_request);
...
where flag is an extra column added to the cursor result as e.g. , ROWNUM AS FLAG - just as a mechanism to know if you are on the first row or a subsequent row (and so need as comma between array elements).
The dbms_output lets you see the entire generated value, which you can manually inspect and/or pass through a JSON validator to check it is what you (and more importantly the receiving service) expect to see. You can still add newlines during the JSON construction for readability, though the service won't care about those.
Depending on how many rows you could have you might need l_string_request to be a CLOB instead of a varchar2. I'd also suggest you explicitly format date/timestamp values, and handle numbers properly if you have any of those.
After modification according to your advice I'm getting this error:
ORA-06502: PL/SQL: numeric or value error ORA-06512: at "OPERA_MARRIOTT.HRS_LASERLINE_BI_PRIHOD", line 87
You would get this if l_clob_response is empty, so perhaps your read loop isn't getting what you expect now. (Which may well be because the request is now invalid JSON, or doesn't match what the service expects). After the UTL_HTTP.get_response() call, see what l_http_response.status_code and l_http_response.reason_phrase show. The status codes are in the documentation.

passing a clob to JSon_object_t

How do i pass a {Base64 encoded content} value in a json object. the file that i want to send is a blob type in my Oracle Table.
FOLLOWING snippets of my code
Cursor csr_con_attach is
select file_name documentName,file_blob ,
to_char(created_date,'DD-MON-YYYY')||'T'||to_char(created_date,'HH24:MI:SS') documentDate
From contact.con_attachments
WHERE con_header_id_fk = 2770;
e.g.
For rec_con_attach in csr_con_attach
Loop
v_obj_build :=
json_object_t
('{'
|| '"documentName"' || ':"' || rec_con_attach.documentName || '",'
|| '"blobData"' || ':"' || rec_con_attach.file-blob ||'",'
|| '"documentNotes"' || ':"' || 'Test' ||'",'
|| '"documentDate"' || ':"' || rec_con_attach.documentDate
||'"'||
'}');
end loop;
When i compile this I get an error "wrong number or type of arguments in call to '||" . The rest API that i am calling expects a {Base64 encoded content} for blobData.
I also tried converting blob file to base64encode(using procedure below), but then i get JSONT syntax error ORA-06512 sys.JDOM_T
Declare
l_step pls_integer := 22500; -- make sure you set a multiple of 3 not higher than 24573
l_clob clob;
begin
for i in 0 .. trunc((dbms_lob.getlength(i_blob) - 1 )/l_step)
loop
dbms_output.put_line(i);
l_clob := utl_raw.cast_to_varchar2(utl_encode.base64_encode(dbms_lob.substr(i_blob, l_step, i * l_step + 1)));
end loop;
io_clob := l_clob;
end;
Any help would be appreciated
Thanks

Want to send excel as mail attachment through pl/sql procedure

When I send mail with attachment through procedure using clob to create data.
For small data it's working fine. But for large data it's misbehaving (alignment changing). can you help whether anything needs to done with the code.
create or replace PROCEDURE RPT AS
l_clob clob;
l_bfile bfile;
l_fhandle utl_file.file_type;
l_buffer VARCHAR2(8192);
v_count number;
a_count number;
k_count number;
ka_count number;
currentdate DATE;
non_work_days_count number;
BEGIN
--preparing header
dbms_lob.createtemporary (l_clob, TRUE);
l_clob := l_clob
|| 'Column1'|| ','
|| 'Column2' || ','
|| 'Column3' || ','
|| 'Column4' || ','
|| UTL_TCP.crlf;
for crq in (select col1,col2,col3,col4 from table where id=1 ) loop
/* Prepare Details data using Clob */
l_clob := l_clob
|| to_clob(crq.COl1)|| ','
|| to_clob(crq.COL2) || ','
|| to_clob(crq.COL3) || ','
|| to_clob(crq.COL4) || ','
|| UTL_TCP.crlf;
end loop;
for crq in (select col1,col2,col3,col4 from table where id=2 ) loop
/* Prepare Details data using Clob */
l_clob := l_clob
|| to_clob(crq.COl1)|| ','
|| to_clob(crq.COL2) || ','
|| to_clob(crq.COL3) || ','
|| to_clob(crq.COL4) || ','
|| UTL_TCP.crlf;
end loop;
dbms_output.put_line('Sending mail with attachment ');
ATTACHMENT_SEND(p_to=> 'req#Email.com',
p_from=> 'req#Email.com',
p_subject=> ' Report ',
p_text_msg=>'Hi All',
p_attach_name =>'report_'||sysdate||'.csv',
p_attach_mime =>'text/plain',
p_attach_clob =>l_clob,
p_smtp_host=>'host.com');
dbms_lob.freetemporary(l_clob);
END RPT;
Also I observe when character length reach 32000 it's breaking(leaving remaining data).
The data in the created excel attachment is having alignment issue
After searching a lot, I found a different approach which worked as a gem for me(which can handle lakhs of records). I thought it would help someone someday.
Steps:
1)Created a directory (in which the expected excel is stored).
2)Write the output of the select statement into the above file.
3)Pick the file at above location and send it in the mail attachment.
Note: if any one needs SMTP code part or any assistance let me know
|| concatenation limits you up to 4000 in Oracle SQL while limits to 32000 in PL/SQL. You may try [DBMS_LOB.APPEND][1] procedure to concatenate long CLOBs. So you may try to convert your code to -
CREATE OR REPLACE PROCEDURE RPT AS
l_clob clob;
l_bfile bfile;
l_fhandle utl_file.file_type;
l_buffer VARCHAR2(8192);
v_count number;
a_count number;
k_count number;
ka_count number;
currentdate DATE;
non_work_days_count number;
BEGIN
--preparing header
dbms_lob.createtemporary (l_clob, TRUE);
l_clob := l_clob
|| 'Column1'|| ','
|| 'Column2' || ','
|| 'Column3' || ','
|| 'Column4' || ','
|| UTL_TCP.crlf;
for crq in (select col1,col2,col3,col4 from table where id=1 ) loop
/* Prepare Details data using Clob */
l_clob := DBMS_LOB.APPEND(
DBMS_LOB.APPEND(
DBMS_LOB.APPEND(
DBMS_LOB.APPEND(l_clob
,to_clob(crq.COl1) || ',')
,to_clob(crq.COL2) || ',')
,to_clob(crq.COL3) || ',')
,to_clob(crq.COL4) || ',' || UTL_TCP.crlf);
end loop;
for crq in (select col1,col2,col3,col4 from table where id=2 ) loop
/* Prepare Details data using Clob */
l_clob := DBMS_LOB.APPEND(
DBMS_LOB.APPEND(
DBMS_LOB.APPEND(
DBMS_LOB.APPEND(l_clob
,to_clob(crq.COl1) || ',')
,to_clob(crq.COL2) || ',')
,to_clob(crq.COL3) || ',')
,to_clob(crq.COL4) || ',' || UTL_TCP.crlf);
end loop;
dbms_output.put_line('Sending mail with attachment ');
ATTACHMENT_SEND(p_to => 'req#Email.com',
p_from => 'req#Email.com',
p_subject => ' Report ',
p_text_msg =>'Hi All',
p_attach_name =>'report_'||sysdate||'.csv',
p_attach_mime =>'text/plain',
p_attach_clob =>l_clob,
p_smtp_host =>'host.com');
dbms_lob.freetemporary(l_clob);
END RPT;
[1]: https://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_lob.htm#i997432

Oracle PLSQL invalid cursor error I don't understand

I'm still a relatively newbe when it comes to PL/SQL.
Using Oracle 12c on Linux RHEL 6.8, the following shell script will attempt to activate all RI constraints in a collection of tables, and if they fail with parent key failures, it will dump the first 100 rows (or less) of the offending data. Or at least that is the goal. Since the script deals mostly with system tables on 12c (with only a small user table list that is unique to my installation), I'm including the whole thing exactly from my environment.
The main work occurs in the exception handling where the system tables are queried for the constraint, and user queries are formed from those data.
As a extra goal, the output is rather messy and I want to clean it up, but first it has to work :)
The output / error I get for my tables is the following:
ERROR Handling here for table NRNG_MTC_VST Constraint Name:
SYS_C0011790 Final SQL = SELECT DISTINCT NRNG_MTC_VST.LOG_CRT_DT ,
NRNG_MTC_VST.NRRNG_MTC_LG_ID FROM ODB_PRIMARY.NRNG_MTC_VST WHERE NOT
EXISTS (SELECT 1 FROM ODB_PRIMARY.NRNG_MTC_LOG WHERE
NRNG_MTC_VST.LOG_CRT_DT = NRNG_MTC_LOG.LOG_CRT_DT AND
NRNG_MTC_VST.NRRNG_MTC_LG_ID = NRNG_MTC_LOG.NRRNG_MTC_LG_ID) FETCH
FIRST 100 rows only
---xxx End SQL DECLARE
* ERROR at line 1: ORA-01001: invalid cursor ORA-06512: at line 111 ORA-02298: cannot validate (ODB_PRIMARY.SYS_C0011790) - parent keys
not found
The output SQL from my print_line is correct, and would work if pasted directly into a SQLDeveloper session. There is just something silly about how the cursor is defined I don't understand.
The full text of the script. BYW, if you see other bonehead changes that should be made unrelated to the error, please suggest them as well.
cd $OGGHOME/scripts
export ORACLE_SID=odbod07 $ORACLE_HOME/bin/sqlplus <<-EOF / as sysdba
alter session set container=p01_odbod07;
set echo on set feedback on
set heading off
set serveroutput on size 10000
DECLARE finalsql varchar2(2048);
part1sql varchar2(1024) ;
part2sql varchar2(1024) := ' ';
cownername varchar2(1024);
ctablename varchar2(1024);
pownername varchar2(1024);
ptablename varchar2(1024);
cnt number := 0;
-- Weak cursor defs
my_cursor sys_refcursor;
BEGIN FOR i in (
select owner, table_name, constraint_name
from dba_constraints
where constraint_type = 'R'
and status = 'DISABLED'
and owner = 'ODB_PRIMARY'
and TABLE_NAME in
-- enter user tables with RI constraints here
('RRNG_MTC_STN_CPLY',
'NRNG_MTC_VST_MTRL_USG',
'NRNG_MTC_VST',
'CAR_CORE',
'NRNG_MTC_LOG'))
-- end user table definitions, rest of code should rely only on system tables
LOOP BEGIN
dbms_output.put_line('alter table '||i.owner|| '.' ||
i.table_name || ' enable constraint '||i.constraint_name);
execute immediate 'alter table '||i.owner|| '.' ||
i.table_name || ' enable constraint '||i.constraint_name;
EXCEPTION
-- exception handling - dump offending data
WHEN OTHERS THEN -- take all exceptions for now
dbms_output.put_line ('ERROR Handling here for table ' ||
i.table_name || ' Constraint Name: ' ||i.constraint_name);
finalsql := 'SELECT DISTINCT ';
part1sql := '';
part2sql := ' ';
cnt := 0;
for constraint in (
SELECT ucc1.OWNER as childowner,
ucc1.TABLE_NAME as childtable,
ucc1.column_name as childcolumn,
ucc2.OWNER as parentowner,
ucc2.TABLE_NAME as parenttable,
ucc2.column_name as parentcolumn,
utc1.data_type as childdatatype,
utc1.data_length as childdatalen
FROM all_constraints uc ,
all_cons_columns ucc1 ,
all_cons_columns ucc2,
all_tab_columns utc1
WHERE
uc.constraint_name = ucc1.constraint_name
AND uc.r_constraint_name = ucc2.constraint_name
AND ucc1.POSITION = ucc2.POSITION
AND ucc1.table_name = utc1.table_name
AND ucc1.column_name = utc1.column_name
AND uc.constraint_type = 'R'
AND uc.constraint_name = i.constraint_name
ORDER BY ucc1.TABLE_NAME , uc.constraint_name)
loop
cownername := constraint.childowner;
ctablename := constraint.childtable;
pownername := constraint.parentowner;
ptablename := constraint.parenttable;
if cnt > 0 then
part1sql := part1sql || ' , ';
part2sql := part2sql || ' AND ';
end if;
part1sql := part1sql || constraint.childtable ||
'.'||constraint.childcolumn || ' ';
part2sql := part2sql || constraint.childtable || '.'
|| constraint.childcolumn || ' = '
|| constraint.parenttable || '.' ||
constraint.parentcolumn;
cnt := cnt + 1;
end loop;
finalsql := finalsql || part1sql ||
' FROM ' || ' ' || cownername ||
'.' || ctablename ||
' WHERE NOT EXISTS (SELECT 1 FROM ' ||
pownername || '.' || ptablename ||
' WHERE ' || part2sql || ') FETCH FIRST 100 rows only';
dbms_output.put_line ('Final SQL = ' || finalsql);
dbms_output.put_line ('---xxx End SQL');
open my_cursor for finalsql;
dbms_sql.return_result(my_cursor);
close my_cursor;
-- EXECUTE IMMEDIATE finalsql;
END;
end loop; end;
/
EOF
Many thanks for any help provided.
Brian
Just to narrow this down to a simple test case, I think this is the error you are seeing:
declare
my_cursor sys_refcursor;
begin
open my_cursor for 'select ''Hello, world'' as message from dual';
dbms_sql.return_result(my_cursor);
close my_cursor; -- << Remove this line
end;
/
ERROR at line 1:
ORA-01001: invalid cursor
ORA-06512: at line 6
This is because you attempted to close the cursor when you have already passed it to dbms_sql for processing. Remove the line with close my_cursor.
declare
my_cursor sys_refcursor;
begin
open my_cursor for 'select ''Hello, world'' as message from dual';
dbms_sql.return_result(my_cursor);
end;
/
PL/SQL procedure successfully completed.
ResultSet #1
MESSAGE
------------
Hello, world
1 row selected.
I had same kind of issue when i tried to print Ref_cursor directly. Then i created a Record type variable and then fetched field values in that variable and then i used DBMS_OUTPUT for that record type variable.
Please see if below code and scenario can help you-
set serveroutput on;
declare
v_sql varchar2(1000);
v_cursor sys_refcursor;
type myrec is record(col1 varchar2(100),col2 varchar2(1000));
rec myrec;
begin
v_sql:='select name,status from t_employee where user_id in (''C001117'',''C001122'')';
open v_cursor for v_sql;
loop
fetch v_cursor
into rec;
exit when v_cursor%notfound;
dbms_output.put_line( rec.col1||':status '||rec.col2 );
end loop;
end;
/
The following is my semi-complete script. Given a table list, it will attempt to activate the RI Constraints, and if they fail it will print out the FK data records in the child table that prevent it from being applied.
The hardest part of this project was the fact that the FKs can be any number of columns and of any type, so the print the results of the select in this case was very tricky (IMO).
Thanks for the help people provided.
cd $OGGHOME/scripts
. ./functions.sh
$ORACLE_HOME/bin/sqlplus ${ORACLE_USERID}/${ORACLE_PASSWORD}#${ORACLE_SID} << EOF
set echo on
set feedback on
set heading off
set serveroutput on size unlimit
DECLARE
finalsql varchar2(2048);
part1sql varchar2(1024) ;
part2sql varchar2(1024) := ' ';
cownername varchar2(1024);
ctablename varchar2(1024);
pownername varchar2(1024);
ptablename varchar2(1024);
cnt number := 0;
desc_tab dbms_sql.desc_tab;
col_count INTEGER;
cursor_name INTEGER;
-- Weak cursor defs
my_cursor sys_refcursor;
col1 varchar2(50);
d number;
j number;
lineout varchar2(2048);
plineout varchar2(2048);
rows number;
eCount number := 0;
BEGIN
FOR i in (
select owner, table_name, constraint_name
from dba_constraints
where constraint_type = 'R'
and status = 'DISABLED'
and owner = '$DBSCHEMA'
and TABLE_NAME in (
'RRNG_MTC_STN_CPLY',
'NRNG_MTC_VST_MTRL_USG',
'NRNG_MTC_VST',
'MTC_TSK_HRHY'))
LOOP
BEGIN
dbms_output.put_line ('.');
dbms_output.put_line ('=====================================');
dbms_output.put('alter table '||i.owner|| '.' || i.table_name || ' enable constraint '||i.constraint_name);
execute immediate 'alter table '||i.owner|| '.' || i.table_name || ' enable constraint '||i.constraint_name;
dbms_output.put_line (' ... SUCCESS');
EXCEPTION -- exception handling - dump offending data
WHEN OTHERS THEN
eCount := eCount + 1;
dbms_output.put_line (' ... FAILED. Constraint Name: ' || i.constraint_name);
finalsql := 'SELECT DISTINCT ';
part1sql := '';
part2sql := ' ';
cnt := 0;
for constraint in (
SELECT ucc1.OWNER as childowner,
ucc1.TABLE_NAME as childtable,
ucc1.column_name as childcolumn,
ucc2.OWNER as parentowner,
ucc2.TABLE_NAME as parenttable,
ucc2.column_name as parentcolumn,
utc1.data_type as childdatatype,
utc1.data_length as childdatalen
FROM all_constraints uc ,
all_cons_columns ucc1 ,
all_cons_columns ucc2,
all_tab_columns utc1
WHERE
uc.constraint_name = ucc1.constraint_name
AND uc.r_constraint_name = ucc2.constraint_name
AND ucc1.POSITION = ucc2.POSITION
AND ucc1.table_name = utc1.table_name
AND ucc1.column_name = utc1.column_name
AND uc.constraint_type = 'R'
AND uc.constraint_name = i.constraint_name
ORDER BY ucc1.TABLE_NAME ,
uc.constraint_name)
loop
cownername := constraint.childowner;
ctablename := constraint.childtable;
pownername := constraint.parentowner;
ptablename := constraint.parenttable;
if cnt > 0 then
part1sql := part1sql || ' , ';
part2sql := part2sql || ' AND ';
end if;
part1sql := part1sql || constraint.childtable || '.' || constraint.childcolumn || ' ';
part2sql := part2sql || constraint.childtable || '.' || constraint.childcolumn || ' = '
|| constraint.parenttable || '.' || constraint.parentcolumn;
cnt := cnt + 1;
end loop;
finalsql := finalsql || part1sql || ' FROM ' || ' ' || cownername || '.' || ctablename || ' WHERE NOT EXISTS (SELECT 1 FROM ' ||
pownername || '.' || ptablename || ' WHERE ' || part2sql || ') FETCH FIRST 100 rows only';
dbms_output.put_line ('Final SQL = (' || finalsql || ')');
-- dbms_output.put_line ('---xxx End SQL');
lineout := 'Child Table: ' || ctablename || '(';
plineout := 'Parent Table: ' || ptablename;
cursor_name := dbms_sql.open_cursor;
dbms_sql.PARSE (cursor_name, finalsql, DBMS_SQL.NATIVE);
d := dbms_sql.execute (cursor_name);
dbms_sql.describe_columns (cursor_name, col_count, desc_tab);
for j in 1..col_count
LOOP
DBMS_SQL.DEFINE_COLUMN (cursor_name, j, col1, 30);
lineout := lineout || desc_tab(j).col_name || ' , ';
-- plineout := plineout || constraint.parentcolumn || ' ';
-- dbms_output.put_line ('Column 1: ' || j || ' is ' || desc_tab(j).col_name || ' type '
-- || desc_tab(j).col_type);
END LOOP j;
lineout := lineout || ')';
-- plineout := plineout || ')';
dbms_output.put_line (lineout);
dbms_output.put_line (plineout);
lineout := NULL;
for j in 1..col_count
LOOP
if j > 1 then
lineout := lineout || ' ';
end if;
lineout := lineout || desc_tab(j).col_name;
END LOOP;
dbms_output.put_line (lineout);
dbms_output.put_line ('----------------------------------------');
LOOP
rows := dbms_sql.fetch_rows (cursor_name);
EXIT WHEN rows = 0;
lineout := NULL;
for j in 1..col_count
LOOP
dbms_sql.column_value (cursor_name, j, col1);
if j > 1 then
lineout := ltrim(lineout || ' ' || col1);
else
lineout := col1;
END IF;
END LOOP;
dbms_output.put_line (lineout);
END LOOP;
dbms_sql.close_cursor (cursor_name);
END;
end loop;
end;
/
EOF
your FETCH FIRST 100 rows only would seem to be out of place.
This is part of the BULK COLLECT clause in a SELECT statement in PL/SQL; as far as I know, it is not part of a SQL statement you can pass into a cursor like this
This is resulting in the cursor statement being invalid

Killing a task doesn't unlock tables in SQL Developer

I'm using Oracle SQL Developer version 3.0.04 to interface with an 11g database over a VPN to my company server. I've noticed that after killing a task via the Task Progress window, it does not actually "kill" the task outright. I've read in some forums that what is going on is that Oracle is trying to "roll back" the changes made by the procedure I'm calling, but I can't imagine it's taking more than 10 minutes to roll back to my last commit after 5 minutes of run time. This leads me to believe that it is continuing to run anyway.
I can message the DBA to kill my session, which frees up the lock on the tables the process was using (as well as the source code of the package so I can edit it again), but this is not an optimal solution. I do not have command-line access to the DB, and I do not have administrator privileges through SQL Developer. Is there any way to definitively kill the task I started, a la kill -9?
I use this generic thinggy to see whats running in the schema, and use it to monitor
rollback. It uses concepts stolen from various places on the net, including ask tom.
DECLARE
SID INTEGER;
LOCKWAIT VARCHAR2(100);
OSUSER VARCHAR2(32);
PROGRAM VARCHAR2(48);
LOGIN_TIME DATE;
SQL_TEXT VARCHAR2(32760);
estimated_rollback_megs INTEGER;
STATUS VARCHAR2(8);
OSUSERX VARCHAR2(100);
PROGRAMX VARCHAR2(100);
SIDX INTEGER;
STATUSX VARCHAR2(100);
MACHINEX VARCHAR2(100);
LOGON_TIMEX DATE;
LOCKED_OBJECT_NAME VARCHAR2(100);
processcount integer := 0;
CURSOR raw_data IS
WITH
sessionInfo AS
(
SELECT *
FROM V$SESSION
WHERE USERNAME = USER -- Just data for my current schema
ORDER BY SID
)
SELECT si.SID,
si.LOCKWAIT,
si.OSUSER,
si.PROGRAM,
si.LOGON_TIME,
si.STATUS,
(
SELECT ROUND(USED_UBLK*8/1024,1)
FROM V$TRANSACTION,
sessionInfo
WHERE sessionInfo.TADDR = V$TRANSACTION.ADDR
AND sessionInfo.SID = si.SID
) estimated_rollback_megs,
(
SELECT (MAX(DECODE(PIECE, 0,SQL_TEXT,NULL)) ||
MAX(DECODE(PIECE, 1,SQL_TEXT,NULL)) ||
MAX(DECODE(PIECE, 2,SQL_TEXT,NULL)) ||
MAX(DECODE(PIECE, 3,SQL_TEXT,NULL)) ||
MAX(DECODE(PIECE, 4,SQL_TEXT,NULL)) ||
MAX(DECODE(PIECE, 5,SQL_TEXT,NULL)) ||
MAX(DECODE(PIECE, 6,SQL_TEXT,NULL)) ||
MAX(DECODE(PIECE, 7,SQL_TEXT,NULL)) ||
MAX(DECODE(PIECE, 8,SQL_TEXT,NULL)) ||
MAX(DECODE(PIECE, 9,SQL_TEXT,NULL)) ||
MAX(DECODE(PIECE, 10,SQL_TEXT,NULL)) ||
MAX(DECODE(PIECE, 11,SQL_TEXT,NULL)) ||
MAX(DECODE(PIECE, 12,SQL_TEXT,NULL)) ||
MAX(DECODE(PIECE, 13,SQL_TEXT,NULL)) ||
MAX(DECODE(PIECE, 14,SQL_TEXT,NULL)) ||
MAX(DECODE(PIECE, 15,SQL_TEXT,NULL)) ||
MAX(DECODE(PIECE, 16,SQL_TEXT,NULL)) ||
MAX(DECODE(PIECE, 17,SQL_TEXT,NULL)) ||
MAX(DECODE(PIECE, 18,SQL_TEXT,NULL)) ||
MAX(DECODE(PIECE, 19,SQL_TEXT,NULL)) ||
MAX(DECODE(PIECE, 20,SQL_TEXT,NULL)) ||
MAX(DECODE(PIECE, 21,SQL_TEXT,NULL)) ||
MAX(DECODE(PIECE, 22,SQL_TEXT,NULL)) ||
MAX(DECODE(PIECE, 23,SQL_TEXT,NULL)) ||
MAX(DECODE(PIECE, 24,SQL_TEXT,NULL)) ||
MAX(DECODE(PIECE, 25,SQL_TEXT,NULL)) ||
MAX(DECODE(PIECE, 26,SQL_TEXT,NULL)) ||
MAX(DECODE(PIECE, 27,SQL_TEXT,NULL)) ||
MAX(DECODE(PIECE, 28,SQL_TEXT,NULL)) ||
MAX(DECODE(PIECE, 29,SQL_TEXT,NULL)))
FROM V$SQLTEXT_WITH_NEWLINES
WHERE ADDRESS = SI.SQL_ADDRESS AND
PIECE < 30
) SQL_TEXT
FROM sessionInfo si;
CURSOR LONG_OPERATIONS(SIDx number) IS
SELECT VL.MESSAGE,
VL.START_TIME,
VL.LAST_UPDATE_TIME,
VL.TIME_REMAINING
FROM V$SESSION_LONGOPS VL,
V$SESSION V
WHERE VL.USERNAME LIKE USER AND
V.SID = VL.SID AND
V.SERIAL# = VL.SERIAL# AND
V.SID = SIDx AND
VL.TIME_REMAINING > 0
ORDER BY LAST_UPDATE_TIME DESC;
message VARCHAR2(100);
startTime DATE;
lastUpdateTime DATE;
timeRemaining VARCHAR2(100);
PROCEDURE print_sql_statement
(
printThis IN VARCHAR2,
Plen IN NUMBER DEFAULT 132,
Pwhsp IN VARCHAR2 DEFAULT -- newline+space+tab+comma
CHR(10) || CHR(32) || CHR(9) || ','
)
IS
NL CONSTANT VARCHAR2(1) := CHR(10); -- newline character (OS-independent)
SP CONSTANT VARCHAR2(1) := CHR(32); -- space character
TB CONSTANT VARCHAR2(1) := CHR(9); -- tab character
CM CONSTANT VARCHAR2(1) := ','; -- comma
substringStart INTEGER := 1; -- start of string to print
substringEnd INTEGER; -- end of substring to print
endOfString INTEGER := LENGTH(printThis); -- end of string to print
newLinePosition INTEGER; -- point where newline found
stringLength INTEGER := GREATEST(LEAST(Plen, 255), 10); -- 10 <= len <= 255!
BEGIN
NULL;
END print_sql_statement;
BEGIN
DBMS_OUTPUT.ENABLE(1000000);
DBMS_OUTPUT.PUT_LINE('Monitor for ' || USER);
OPEN raw_data;
LOOP
FETCH raw_data INTO
SID,
LOCKWAIT,
OSUSER,
PROGRAM,
LOGIN_TIME,
STATUS,
estimated_rollback_megs,
SQL_TEXT;
EXIT WHEN raw_data%NOTFOUND;
processcount := processcount + 1;
DBMS_OUTPUT.PUT_LINE('');
DBMS_OUTPUT.PUT_LINE(OSUSER || ' Owns Session ' || SID || ' STATUS ' || STATUS);
DBMS_OUTPUT.PUT_LINE('Running ' || PROGRAM || ' Since ' || TO_CHAR(LOGIN_TIME,'MM/DD HH24:MI:SS'));
OPEN LONG_OPERATIONS(SID);
LOOP
FETCH LONG_OPERATIONS INTO
message,
startTime,
lastUpdateTime,
timeRemaining;
EXIT WHEN LONG_OPERATIONS%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(' ' || message);
DBMS_OUTPUT.PUT_LINE(' START: ' || TO_CHAR(startTime,'DD HH24:MI:SS') ||
' UPDATE: ' || TO_CHAR(lastUpdateTime, 'DD HH24:MI:SS') ||
' REMAINING: ' || timeRemaining);
DBMS_OUTPUT.PUT_LINE(' ');
END LOOP;
CLOSE LONG_OPERATIONS;
IF estimated_rollback_megs > 0 then
DBMS_OUTPUT.PUT_LINE('Estimated megs of rollback ' || estimated_rollback_megs);
END IF;
IF LOCKWAIT IS NOT NULL THEN
SELECT SID
INTO SIDX
FROM V$LOCK
WHERE ID1 =
(
SELECT ID1
FROM V$LOCK
WHERE KADDR = LOCKWAIT
) AND BLOCK = 1;
DBMS_OUTPUT.PUT_LINE('The session is waiting on a lock held by session ' || SIDX);
select OBJECT_TYPE || ':' || OBJECT_NAME || ':' || SUBOBJECT_NAME THINGY
INTO LOCKED_OBJECT_NAME
from dba_objects
where object_id =
(
select object_id
from v$locked_object
where session_id = SIDX
);
DBMS_OUTPUT.PUT_LINE('The locked object is ' || LOCKED_OBJECT_NAME);
SELECT OSUSER, PROGRAM, SID, STATUS, MACHINE, LOGON_TIME
INTO OSUSERX, PROGRAMX, SIDX, STATUSX, MACHINEX, LOGON_TIMEX
FROM V$SESSION
WHERE SID = SIDX;
DBMS_OUTPUT.PUT_LINE(SIDX || ': ' || OSUSER || ' Using ' || PROGRAMX || ' STATUS ' || STATUSX);
DBMS_OUTPUT.PUT_LINE(SIDX || ': LOGGED IN ON ' || MACHINEX || ' SINCE ' ||
TO_CHAR(LOGON_TIMEX, 'MM/DD HH24:MI:SS'));
END IF;
IF SUBSTR(SQL_TEXT, 1, 16) <> 'WITH SESSIONINFO' THEN
print_sql_statement(SQL_TEXT, 132);
DBMS_OUTPUT.PUT_LINE('');
ELSE
NULL;
END IF;
END LOOP;
CLOSE raw_data;
DBMS_OUTPUT.PUT_LINE(processcount || ' sessions found');
END;

Resources