I am trying to write a procedure which send emails from Oracle DB using UTL_SMTP functions. Its working fine if I am only sending one email address but if I am passing multiple emails as comma or semicollon(;), its failling with error like
ORA-29277: invalid SMTP operation
ORA-29279: SMTP permanent error: 501 5.1.3 Invalid address
I am writing the statement like :- where P_ALERT_DESTINATION is a variable which is having multiple emails id.
UTL_SMTP.WRITE_DATA(L_MAIL_CONN, 'To: ' || P_ALERT_DESTINATION || UTL_TCP.CRLF);
I think you need to loop all email addresses and make that procedure call to each one.
Pseudo code:
for all_addresses loop
UTL_SMTP.WRITE_DATA(L_MAIL_CONN, 'To: ' || one_address|| UTL_TCP.CRLF)
end loop;
Edited:
Here you go, this should work:
declare
cursor split_cursor(p_to in varchar2) is
select regexp_substr(p_to, '[^,]+', 1, level) email_address
from dual
connect by regexp_substr(p_to, '[^,]+', 1, level) is not null;
begin
for i in split_cursor(P_ALERT_DESTINATION) loop
UTL_SMTP.WRITE_DATA(L_MAIL_CONN, 'To: ' || i.email_address|| UTL_TCP.CRLF);
end loop;
end;
You should also remove all spaces if the variable contains those.
Related
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.
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 || );
CREATE OR REPLACE PROCEDURE country_demographics
(p_country_name IN countries.country_name%TYPE,
p_country_demo_rec OUT ed_type)
IS
TYPE ed_type IS RECORD (
c_name countries.country_name%TYPE,
c_location countries.location%TYPE,
c_capitol countries.capitol%TYPE,
c_population countries.population%TYPE,
c_airports countries.airports%TYPE,
c_climate countries.climate%TYPE);
BEGIN
SELECT country_name, location, capitol, population, airports, climate
INTO ed_type.c_name, ed_type.c_location, ed_type.c_capitol, ed_type.population, ed_type.airports, ed_type.climate
FROM countries;
DBMS_OUTPUT.PUT_LINE('Country Name:' || v_country_demo_rec.country_name ||
'Location:' || v_country_demo_rec.location ||
'Capitol:' || v_country_demo_rec.capitol ||
'Population:' || v_country_demo_rec.population ||
'Airports:' || v_country_demo_rec.airports ||
'Climate:' || v_country_demo_rec.climate );
IF SQL%NOTFOUND THEN
RAISE_APPLICATION_ERROR(-20201, 'This country does not exist.');
END IF;
END;
The problem is asking me to create a procedure called country_demograhics. Pass the country_name as an IN parameter. Display CONTRY_NAME, LOCATION, CAPITOL, POPULATION, AIRPORTS, CLIMATE. Use a user-defined record structure for the INTO clause of your select statement. Raise an exception if the country does not exist.
Now here is a copy of my code, that keeps coming back with an error of:
Error at line 0: PL/SQL: Compilation unit analysis terminated.
That error should be the second error which tells you, it will not look any further. There should be another error too. I guess that ed_type doesn't exist outside of the procedure so it can not have an ed_type as OUT parameter. ed_type isn't known outside.
First thing - Look you used the different variable in declaring(p_country_demo_rec ) and begin(v_country_demo_rec) part. I think that might be one mistake.
Try following script:- it may help you.
CREATE OR REPLACE PROCEDURE COUNTRY_DEMOGRAPHICS
IS
TYPE ED_TYPE IS TABLE OF countries%ROWTYPE;
p_country_demo_rec ED_TYPE;
BEGIN
SELECT * BULK COLLECT INTO p_country_demo_rec FROM countries;
FOR i IN p_country_demo_rec.FIRST..p_country_demo_rec.LAST
LOOP
DBMS_OUTPUT.PUT_LINE('Country Name:'||p_country_demo_rec(i).country_name ||
'Location:' || p_country_demo_rec(i).location ||
'Capitol:' || p_country_demo_rec(i).capitol ||
'Population:' || p_country_demo_rec(i).population ||
'Airports:' || p_country_demo_rec(i).airports ||
'Climate:' || p_country_demo_rec(i).climate );
END LOOP;
IF SQL%NOTFOUND THEN
RAISE_APPLICATION_ERROR(-20201, 'This country does not exist.');
END IF;
END;
/
EXECUTE COUNTRY_DEMOGRAPHICS;
Note:- You can use the one parameter(IN parameter) in a procedure to get the specific country demographics data and that parameter use in select statement for filter out the specific country.
Example:
CREATE OR REPLACE PROCEDURE COUNTRY_DEMOGRAPHICS(p_country_name IN varchar2)
Select statement looks like:
SELECT * BULK COLLECT INTO p_country_demo_rec FROM countries where
country_name = ||p_country_name;
Execute part:
EXECUTE COUNTRY_DEMOGRAPHICS(p_country_name);
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 below proc to read all the data from one table and populate it in a grid in .net form.
CREATE OR REPLACE PROCEDURE EVMPDADM.GETALLBATCHES_ARTICLE_57(p_batchstatus OUT XEVMPD_SUBMITTEDBATCH%ROWTYPE )
IS
TYPE batch_status IS TABLE OF XEVMPD_SUBMITTEDBATCH%ROWTYPE INDEX BY PLS_INTEGER;
l_batchstatus batch_status;
BEGIN
SELECT * BULK COLLECT INTO l_batchstatus FROM XEVMPD_SUBMITTEDBATCH ;
FOR i IN 1..l_batchstatus.count LOOP
p_batchstatus:= l_batchstatus(i);
END LOOP;
END GETALLBATCHES_ARTICLE_57;
To test if the proc is running fine I tried to print the data by using below Pl-sql block:
DECLARE
v_batchstatus XEVMPD_SUBMITTEDBATCH%ROWTYPE;
BEGIN
EVMPDADM.GETALLBATCHES_ARTICLE_57(v_batchstatus);
DBMS_OUTPUT.PUT_LINE( v_batchstatus.Batch_id || ' ' || v_batchstatus.BATCH_DESCRIPTION || ' ' || v_batchstatus.STATUS || ' ' ||v_batchstatus.RECORD_STATUS || ' ' ||v_batchstatus.NUMBER_OF_RECORDS);
END;
/
But from this process I am getting the last row only.
I want to print all the records present in the table.
can any one please help me to figure out what is wrong in the above code.
The error messages are very obvious. You are calling your procedures with:
Wrong number of arguments for EVMPDADM.GETALLBATCHES_ARTICLE_57: It has one OUT parameter. So you need to pass that parameter.
Wrong type of argument for DBMS_OUTPUT.PUT_LINE: It has one IN VARCHAR2 parameter, and not XEVMPD_SUBMITTEDBATCH%ROWTYPE. Read here
So, it should be this way:
DECLARE
v_batchstatus XEVMPD_SUBMITTEDBATCH%ROWTYPE;
BEGIN
v_batchstatus:= EVMPDADM.GETALLBATCHES_ARTICLE_57(v_batchstatus);
--use DBMS_OUTPUT.PUT_LINE for every column of XEVMPD_SUBMITTEDBATCH separately after you convert them to varchar2 if they are not.
END;
/
Besides, the procedure this way will return only the last row. So you might want to change that.
If you want to print all the records from the table, you need to add DBMS_OUTPUT.PUT_LINE inside the loop, it will become like this:
FOR i IN 1..l_batchstatus.count LOOP
p_batchstatus:= l_batchstatus(i);
dbms_output.put_line( p_batchstatus.col1 || ' ' || p_batchstatus.col2 || ... );
END LOOP;
Where col1, col2, ... are the columns names of XEVMPD_SUBMITTEDBATCH given they are of the type VARCHAR2. Or you will need extra processing