I have a PL/SQL sproc which sends an email, with addressee and such as parameters. We recently migrated it, to a new environment, and switched it from using an internal mail server to using Office 365. In order to do this I needed to upgrade it to use TLS/SSL, which is now working. But it's getting a weird error now.
All of the authentication code works fine, I can transmit the auth, and all the message data, with no issue. But when i call UTL_SMTP.CLOSE_DATA, it throws ORA-06502: PL/SQL: numeric or value error: character string buffer too small.
This section of the code is unchanged from the old environment, where it was working without issue. By this point, I've already concatenated my variables, so I know it's not an issue with my variable sizes. It seems to be something inside the UTL_SMTP package, but that seems to be a compiled package, so I can't even view the stack source to try to figure out what or where the issue is.
Below is our sproc code...
CREATE OR REPLACE PROCEDURE ourschema.SENDMAILTLS
(
vSENDER IN VARCHAR2,
vSENDEE IN VARCHAR2,
vSUBJECT IN VARCHAR2,
vMESSAGE IN VARCHAR2
) AS
vMAILHOST VARCHAR2(255) := ourschema.GETOPTION('SMTPSRV');
oSMTP UTL_SMTP.connection;
vCRLF VARCHAR2(2) := chr(13) || chr(10);
vDATA VARCHAR2(32767);
BEGIN
vDATA := 'Subject:' || vSUBJECT || vCRLF;
vDATA := vDATA || 'Date:' || to_char(SYSDATE, 'Dy, DD Mon YYYY hh24:mi:ss') || vCRLF;
vDATA := vDATA || 'From:' || vSENDER || vCRLF;
vDATA := vDATA || 'Content-Type:text; charset=us-ascii' || vCRLF;
vDATA := vDATA || 'Reply-To:' || vSENDER || vCRLF;
vDATA := vDATA || 'Sender:' || vSENDER || vCRLF;
vDATA := vDATA || vCRLF;
vDATA := vDATA || vMESSAGE || vCRLF;
vDATA := vDATA || vCRLF;
ourschema.LOG('TLS Email sending from ' || vSENDER || ' to ' || vSENDEE || ' via ' || vMAILHOST || '.', vDATA);
utl_tcp.close_all_connections();
oSMTP := UTL_SMTP.open_connection(vMAILHOST, TO_NUMBER(ourschema.GETOPTION('SMTPPORT')), wallet_path => 'file:O:\ur\Wallet\Path', wallet_password => 'OurWalletPassword');
UTL_SMTP.EHLO(oSMTP, vMAILHOST);
UTL_SMTP.STARTTLS(oSMTP);
UTL_SMTP.EHLO(oSMTP, vMAILHOST);
UTL_SMTP.AUTH(oSMTP, 'U******', 'P******', UTL_SMTP.ALL_SCHEMES);
UTL_SMTP.mail(oSMTP, ourschema.GETOPTION('SMTPADDR'));
UTL_SMTP.rcpt(oSMTP, vSENDEE);
UTL_SMTP.open_data(oSMTP);
UTL_SMTP.write_data(oSMTP, vDATA);
UTL_SMTP.close_data(oSMTP);
UTL_SMTP.quit(oSMTP);
ourschema.LOG('TLS Email sent successfully from ' || vSENDER || ' to ' || vSENDEE || ' via ' || vMAILHOST || '.', vDATA);
END;
The line it is failing on is
UTL_SMTP.close_data(oSMTP);
And this is the test script I'm using. No massive amounts of data that would blow anything out.
begin
-- Call the procedure
idsystem.SENDMAILTLS(vSENDER => 'notifications#ourdomain.com',
vSENDEE => 'myemail#ourdomain.com',
vSUBJECT => 'Testing Oracle Email',
vMESSAGE => 'Did you get this yet?');
end;
And here's the error message... with the stack trace showing it's coming from somewhere deep in the UTL_SMTP package.
But if I say Yes to view the stack source, this is all that comes up for the UTL_SMTP package... so I can't even begin to make heads or tails of how I may be offending it.
Oracle version is 12c Standard, 12.2.0.1.0
Based on the stack trace it looks like the receiver end is sending data greater than 512 characters. This means basically the e-mail that you are sending might have some issue that generates huge data as a response.
The problem might be
recepeint email id is nonexistent
Email attachment is invalid etc
ourschema.GETOPTION('SMTPADDR') should be a valid sender
To understand the issue try sending the same email from your email application and if it success and doesn't generate a large response then please check your configuration.
Related
let's see if somebody can help me, I need to delete rows from different tables and I did think to do it using an array so i wrote this :
DECLARE
TYPE mytype_a IS TABLE OF VARCHAR2(32) INDEX BY BINARY_INTEGER;
mytype mytype_a;
BEGIN
mytype(mytype.count + 1) := 'MYTABLE';
FOR i IN 1 .. mytype.count
LOOP
DELETE mytype(i) WHERE valid = 'N';
END LOOP;
END;
Trying to run this piece of code using sqldeveloper I get the ORA-00933 command not properly ended, if I put directly the table name it works, what am I doing wrong?
Thank you.
Thank you very much guys, it works perfectly.
This is not the correct approach. You have to use Dynamic SQL for this -
DECLARE
type mytype_a is table of varchar2(32) index by binary_integer;
mytype mytype_a;
stmt varchar(500) := NULL;
BEGIN
mytype (mytype.count + 1) := 'MYTABLE';
for i in 1..mytype.count loop
stmt := 'DELETE FROM ' || mytype(i) || ' where valid =''N''';
EXECUTE IMMEDIATE stmt;
end loop;
END;
You would need to use dynamic SQL, concatenating the table name from the collection into the statement, inside your loop:
execute immediate 'DELETE FROM ' || mytype(i) || ' where valid = ''N''';
Or you can put the statement into a variable so you can display it for debugging purposes, and then execute that, optionally with a bind variable for the valid value:
stmt := 'DELETE FROM ' || mytype(i) || ' where valid = :valid';
dbms_output.put_line(stmt);
execute immediate stmt using 'N';
dbms_output.put_line('Deleted ' || sql%rowcount || ' row(s)');
... which I've made also display how many rows were deleted from each table. Note though that you shoudln't rely on the caller being able to see anything printed with dbms_output - it's up to the client whether it shows it.
The whole anonymous block would then be:
DECLARE
type mytype_a is table of varchar2(32) index by binary_integer;
mytype mytype_a;
stmt varchar2(4000);
BEGIN
mytype (mytype.count + 1) := 'MYTABLE';
for i in 1..mytype.count loop
stmt := 'DELETE FROM ' || mytype(i) || ' where valid = :valid';
dbms_output.put_line(stmt);
execute immediate stmt using 'N';
dbms_output.put_line('Deleted ' || sql%rowcount || ' row(s)');
end loop;
END;
/
You could use a built-in collection type to simplify it even further.
db<>fiddle showing some options.
Hopefully this doesn't apply, but if you might have any tables with quoted identifiers then you would need to add quotes in the dynamic statement, e.g.:
stmt := 'DELETE FROM "' || mytype(i) || '" where valid = :valid';
... and make sure the table name values in your collection exactly match the names as they appear in the data dictionary (user_tables.table_name).
I'm trying to create a pl/sql query that fetches certain data and from tables and outputs the data. Here is what I have tried but I keep getting an error and I cannot begin to see where the problem is.
SET SERVEROUTPUT ON
DECLARE
TEMP_CUSTNAME CUSTOMER.FIRST_NAME%TYPE;
TEMP_CUSTSURNAME CUSTOMER.SURNAME%TYPE;
TEMP_COINPUR COIN.PRODUCT%TYPE;
TEMP_CPRICE COIN.PRICE%TYPE;
TEMP_DNOTES COIN_DELIVERY.DELIVERY_NOTES%TYPE;
CURSOR CURSOR1 IS
SELECT C.FIRST_NAME,C.SURNAME FROM CUSTOMER C, COIN.PRODUCT, COIN.PRICE, COIN_DELIVERY.DELIVERY_NOTES
WHERE COIN.PRICE > 8000;
BEGIN
OPEN CURSOR1;
LOOP
FETCH CURSOR1 INTO TEMP_CUSTNAME, TEMP_CUSTSURNAME, TEMP_COINPUR, TEMP_CPRICE, TEMP_DNOTES;
EXIT WHEN CURSOR1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE ('CUSTOMER: ' || TEMP_CUSTNAME || ',' ||TEMP_CUSTSURNAME);
DBMS_OUTPUT.PUT_LINE ('COIN: ' || TEMP_COINPUR || );
DBMS_OUTPUT.PUT_LINE ('PRICE: ' || TEMP_CPRICE || );
DBMS_OUTPUT.PUT_LINE ('NOTES: ' || TEMP_DNOTES || );
DBMS_OUTPUT.PUT_LINE ('------------------------------------' );
END LOOP;
CLOSE CURSOR1;
END;
It would be helpful if you've provided us with the error you're facing.
So far I see number of fields in the cursor (2) is not the same as number of variables being fetched into (5).
In other words, you need to add more columns here:
SELECT C.FIRST_NAME,C.SURNAME FROM CUSTOMER C, COIN.PRODUCT, COIN.PRICE,
so there will be enough data to fetch to this variables here:
FETCH CURSOR1 INTO TEMP_CUSTNAME, TEMP_CUSTSURNAME, TEMP_COINPUR, TEMP_CPRICE, TEMP_DNOTES;
Or, probably, you need to reduce number of variable you're fetching into like this. It depends on result you're trying to achieve
FETCH CURSOR1 INTO TEMP_CUSTNAME, TEMP_CUSTSURNAME;
UPD. wrong concatenation. Based on the error message from comments I see now there are problems here too. Remove trailing "||" in lines below
DBMS_OUTPUT.PUT_LINE ('COIN: ' || TEMP_COINPUR || );
DBMS_OUTPUT.PUT_LINE ('PRICE: ' || TEMP_CPRICE || );
DBMS_OUTPUT.PUT_LINE ('NOTES: ' || TEMP_DNOTES || );
I have the below code which generates a pdf file for each record in the table with an email address to a specific folder, but what I have realized is that on each iteration the URL is appended to the next iteration, which makes the URL increases on each loop. So after the browser reaches its limit it throws an error and generates only 10 files when I am expecting about 1500 pdf files.
PROCEDURE RUN_REPORT2 IS
M_PARAM_ID PARAMLIST ;
M_REP_ID VARCHAR2(3000);
V_REP_CURRENCY VARCHAR2(5);
V_BASE_CURRENCY VARCHAR2(5);
v_show_document VARCHAR2(30000);
v_report_name VARCHAR2(30000);
v_format VARCHAR2(200) := 'PDF';
CURSOR C1 IS
SELECT EMP_EMPLOYEE_NO,
INITCAP(EMP_FIRSTNAME) EMP_FIRSTNAME,
EMP_EMAIL_ADDRESS,
TO_CHAR(PMA_PAY_PERIOD_TO, 'MONTH' || ' ' || 'YYYY') PAY_PERIOD,
EMP_TIN DOB,
TRIM(TO_CHAR(PMA_PAY_PERIOD_TO, 'MONTH')) PAY_MONTH,
TRIM(TO_CHAR(PMA_PAY_PERIOD_TO, 'YYYY')) PAY_YEAR
FROM HR_EMPLOYEES, PAY_PAYROLL_MASTER
WHERE EMP_EMPLOYEE_ID = PMA_EMPLOYEE_ID
AND EMP_COMPANY_CODE = PMA_COMPANY_CODE
AND EMP_EMAIL_ADDRESS IS NOT NULL
AND EMP_EMPLOYEE_NO BETWEEN :ONE.EMPNO_FM AND :ONE.EMPNO_TO
AND TO_CHAR(PMA_PAY_PERIOD_TO, 'Month YYYY') = :ONE.PAY_PERIOD
AND PMA_COMPANY_CODE = :GLOBAL.COMPNAME
BEGIN
SELECT PAR_REPORT_URL, PAR_FORMS_DIRECTORY
INTO v_show_document, v_report_name
FROM APPS_PARAMETERS
WHERE PAR_COMPANY_CODE = :GLOBAL.COMPNAME;
FOR C1_R IN C1 LOOP
v_show_document := v_show_document
|| '&report='||v_report_name||'PAYSLIP_EIC_EMAIL.jsp'
|| '&destype=file'
|| '&desformat='||v_format
|| '&P_EMPNO_FM='||C1_R.EMP_EMPLOYEE_NO
|| '&P_EMPNO_TO=' ||C1_R.EMP_EMPLOYEE_NO
|| '&P_DEPT_FM='||:ONE.DEPT_FM
|| '&P_DEPT_TO=' ||:ONE.DEPT_TO
|| '&P_REG_FM='||:ONE.REG_FM
|| '&P_REG_TO=' ||:ONE.REG_TO
|| '&P_REP_CURRENCY=' ||V_REP_CURRENCY
|| '&P_PERIOD='||:ONE.PAY_PERIOD
|| '&P_COMPCODE=' ||:GLOBAL.COMPNAME
|| '&desname='||v_report_name||'EMAIL_FOLDER\'||C1_R.EMP_EMAIL_ADDRESS||' '||C1_R.EMP_FIRSTNAME||'
'||C1_R.PAY_MONTH||C1_R.PAY_YEAR||'.PDF';
WEB.SHOW_DOCUMENT(v_show_document||'&cmdkey=userlogin','_blank');
SET_APPLICATION_PROPERTY(CURSOR_STYLE, 'DEFAULT');
DESTROY_PARAMETER_LIST(M_PARAM_ID) ;
CLEAR_MESSAGE;
END LOOP;
END;
The way I understood it, this:
v_show_document := v_show_document
|| '&report='||v_report_name||'PAYSLIP_EIC_EMAIL.jsp'
should be
v_show_document :=
'&report='||v_report_name||'PAYSLIP_EIC_EMAIL.jsp'
Otherwise, you're concatenating v_show_document with previous loop's iteration every time, making it HUGE (and more or less useless).
We are forced to use mailx command from PL/SQL for sending mail.
Actually I have the generic script with me, but I am not sure how to execute directly in the PL/SQL code.
The code we have is
(echo "$mailbody" uuencode $ZIP_FILE $ZIP_FILE) | mailx -m -s "subject" -r " " "$mail_to_address"
Obviously you've explained that Oracle has built-in support through UTL_MAIL and it's foolhardy to not use the functionality your organization has already paid for :)
" I am not sure how to execute directly in the PL/SQL code"
You can't run OS commands directly from PL/SQL. However...
Since Oracle 10g, DBMS_SCHEDULER has allowed us to call host programs. CREATE_PROGRAM() where program_type => 'EXECUTABLE'. The scheduler approach would be the best approach if you want to have a background job which polls for notifications and then sends batches of emails. Frankly the best intro the DBMS_SCHEDULER is Tim Hall's articles on Oracle-Base.
But if you need on-demand execution of calls then the approach you need is a Java Stored Procedure which uses a Java Command object to run host calls. There can be political issues with this, because some DBAs are suspicious of Java, but it's supported. Here's an Oracle White Paper which explains how to go about it.
You can send email from a PL/SQL procedure without invoking an OS command like mailx. Some examples are here:
http://www.orafaq.com/wiki/Send_mail_from_PL/SQL
Example code from one of the examples:
DECLARE
v_From VARCHAR2(80) := 'oracle#mycompany.com';
v_Recipient VARCHAR2(80) := 'test#mycompany.com';
v_Subject VARCHAR2(80) := 'test subject';
v_Mail_Host VARCHAR2(30) := 'mail.mycompany.com';
v_Mail_Conn utl_smtp.Connection;
crlf VARCHAR2(2) := chr(13)||chr(10);
BEGIN
v_Mail_Conn := utl_smtp.Open_Connection(v_Mail_Host, 25);
utl_smtp.Helo(v_Mail_Conn, v_Mail_Host);
utl_smtp.Mail(v_Mail_Conn, v_From);
utl_smtp.Rcpt(v_Mail_Conn, v_Recipient);
utl_smtp.Data(v_Mail_Conn,
'Date: ' || to_char(sysdate, 'Dy, DD Mon YYYY hh24:mi:ss') || crlf ||
'From: ' || v_From || crlf ||
'Subject: '|| v_Subject || crlf ||
'To: ' || v_Recipient || crlf ||
crlf ||
'some message text'|| crlf || -- Message body
'more message text'|| crlf
);
utl_smtp.Quit(v_mail_conn);
EXCEPTION
WHEN utl_smtp.Transient_Error OR utl_smtp.Permanent_Error then
raise_application_error(-20000, 'Unable to send mail', TRUE);
END;
/
I have created a standard report page and selected the "cards" for the Report Template (in the Layout and Presentation).
The following is the code that loads the data
DECLARE
l_query VARCHAR2(4000);
l_app number := v('APP_ID');
l_session number := v('APP_SESSION');
//Bug happens on the ':11:' part, page 1 works fine
l_url VARCHAR2(500) := (APEX_UTIL.PREPARE_URL(
p_url => 'f?p=' || l_app || ':11:' || l_session || '::NO:::',
p_checksum_type => 'SESSION'));
BEGIN
l_query:=
'SELECT
post_id,
user_id CARD_SUBTEXT,
image CARD_IMAGE,
title CARD_TITLE,
''' || l_url || ''' CARD_LINK,
text CARD_TEXT
FROM posts';
IF v('P1_TEXT_SEARCH') IS NOT NULL THEN
l_query := l_query||' '||'
WHERE
(
CONTAINS(title, ''' || v('P10_TEXT_SEARCH') || ''') > 0
) OR
(
CONTAINS(text, ''$' || v('P10_TEXT_SEARCH') || ''') > 0
)
';
END IF;
htp.p(l_url || ': ' || l_query);
RETURN l_query;
END;
The l_url variable is my attempt to load the "Post" page which will eventually have the post_id sent in the URL. The page number for the Post page is 11.
When I use "1" (Home page) as the page number it worked fine. But when I used 11 an odd error occurs
Firstly the standard error
1 error has occurred
- Query cannot be parsed within the Builder. If you believe your query is syntactically correct, check the ''generic columns'' checkbox below the region source to proceed without parsing. ORA-00911: invalid character
But the odd part is a line of text is spat out at the very top of the application that says the following:
javascript:apex.navigation.dialog('f?p=4000:11:15325469163221::NO:::\u0026p_dialog_cs=Q1H4HM_OXFo_ZS45s-NOciyBPvE0vUNqa7JH2d-wczZD8Yom-OFjYOrWO4XNE6ciYtHJ0MCQL8cbir4OVFGtUg',{title:'Create Master Detail',height:'480',width:'800',maxWidth:'1200',modal:true,dialog:null,resizable:true,minWidth:500,minHeight:400},'a-Dialog--wizard',this);: SELECT post_id, user_id CARD_SUBTEXT, image CARD_IMAGE, title CARD_TITLE, 'javascript:apex.navigation.dialog('f?p=4000:11:15325469163221::NO:::\u0026p_dialog_cs=Q1H4HM_OXFo_ZS45s-NOciyBPvE0vUNqa7JH2d-wczZD8Yom-OFjYOrWO4XNE6ciYtHJ0MCQL8cbir4OVFGtUg',{title:'Create Master Detail',height:'480',width:'800',maxWidth:'1200',modal:true,dialog:null,resizable:true,minWidth:500,minHeight:400},'a-Dialog--wizard',this);' CARD_LINK, text CARD_TEXT FROM posts Content-Type:text/html; charset=utf-8 Cache-Control:no-store Pragma:no-cache Expires:Sun, 27 Jul 1997 13:00:00 GMT X-Frame-Options:DENY
It's just plain and doesn't look like it's meant to happen.
I tried copying page 11 and trying it on page 12 but that didn't work.
The odd error looks like it could be fixed by allowing embed in frames.
http://www.danielmcghan.us/2011/08/new-browser-security-attributes-in-apex.html
The query parse may have failed if l_url contains quotes, so maybe try
replace(url,'''','"')
And I don't think there's no need to use v() in these locations
IF :P10_TEXT_SEARCH IS NOT NULL THEN -- this also referred to different page
l_app number := :APP_ID;
CONTAINS(title, '$'||:P10_TEXT_SEARCH) > 0