TRIGGER BEFORE A COMMIT (INSERT OR UPDATE) - oracle

I have a trigger that calls a webservice link.
This link read a View, and that view is composing a XML.
The problema is:
The trigger is executed when I have a cod_situation = 6 like this:
CREATE OR REPLACE TRIGGER trg_candidato_chama_link
AFTER INSERT OR UPDATE ON cand_proc_sel
FOR EACH ROW DECLARE
v_url VARCHAR2(4000);
req UTL_HTTP.REQ;
resp UTL_HTTP.RESP;
value VARCHAR2(1536);
--temp
v_count INTEGER;
v_alternativas VARCHAR2(1000);
v_error VARCHAR2(4000);
BEGIN
if (:new.cod_situation = 6 ) THEN
v_url := 'http://.../frameweb/amxv7/amx_new_employee';
req := UTL_HTTP.BEGIN_REQUEST(v_url);
UTL_HTTP.SET_HEADER(req, 'User-Agent', 'Mozilla/4.0');
resp := UTL_HTTP.GET_RESPONSE(req);
LOOP
UTL_HTTP.READ_LINE(resp, value, TRUE);
DBMS_OUTPUT.PUT_LINE(value);
END LOOP;
UTL_HTTP.END_RESPONSE(resp);
END IF;
EXCEPTION WHEN UTL_HTTP.END_OF_BODY THEN
UTL_HTTP.END_RESPONSE(resp);
INSERT INTO integratio_log(data, cod_integracao, status, rotina, obs) VALUES(SYSDATE, 7, 'SUCCESS', 'TEST DOM, TRIGGER URL', 'Url : ' || v_url || ' count test: '|| v_count); WHEN OTHERS THEN
v_error := To_Char(SQLERRM);
INSERT INTO integracao_log(data, cod_integracao, status, rotina, obs) VALUES(SYSDATE, 7, 'FAIL', 'TEST DOM, TRIGGER URL', 'Url : ' || v_url || ' # qtd: '|| v_count || ' # Erro: ' || v_error);
END;
But, the problem is:
the clause where in the View is just the condition cod_situation = 6, but when the trigger calls the webservice where we read the View, I do not have cod_situation = 6 yet.
So, my question is, how can I call the trigger link, but only after the commit is done in the table?

A post commit hook is not available. And this is for good reason: At the point where you access the web service the modified data has already been committed. What should the database do if the commit is successful but the post commit fails?
Generally speaking it is not a good idea to access other systems from a trigger: Your session is still not committed hence a rollback is possible. That would means that your web service has been informed about a transaction in the database that might never be written to disk. This may or may not be catastrophic. Additionally you make the functionality of your database dependent on the availability of the web service which is bad practice in itself.
If you absolutely have to do what you are doing you would do yourself a favor by moving the view reading into a stored procedure and call that from the trigger and the web service.

Related

Cancel Order API Error - Validation Failed For The Field Change Reason

I'm using the following code to cancel a sales order header using API OE_ORDER_PUB
But always returned Validation failed for the field - Change Reason
DECLARE
--variable declare
BEGIN
-- Setting the Enviroment --
FOR I
IN (SELECT oh.header_id order_header_id
FROM oe_order_headers_all oh
WHERE oh.order_number IN (12301000013) AND oh.org_id = v_org_id)
LOOP
BEGIN
-- CANCEL HEADER --
v_header_rec.cancelled_flag := 'Y';
v_header_rec.change_reason := 'Not Provided';
DBMS_OUTPUT.put_line ('Starting cancel of API');
-- CALLING THE API TO CANCEL AN ORDER --
OE_ORDER_PUB.PROCESS_ORDER (
x_header_rec => v_header_rec_out
--... using the parameter
);
END LOOP;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line ('error in block. reason is :' || SQLERRM);
END;
How could i find the requirement rules for change reason field in OE_ORDER_HEADERS ?
There is not much info here to work with but here is what you could try
Check if there are validations on the page. The most probably cause of this error is that there is a page item with the label "Cancel Reason" that has a validation on it. That validation is failing which raises this validation error
Check if the procedure OE_ORDER_PUB.PROCESS_ORDER has any code to verify if the order can be cancelled.
Also, DBMS_OUTPUT.put_line cannot be used in apex pl/sql code. DBMS_OUTPUT prints message to the buffer of the client (sqlplus/sqldeveloper/apex sql workshop). In apex those calls will not do anything but they should be removed from the code since they could cause buffer overflows.
To properly instrument your code, use APEX_DEBUG to add comments that can be seen in debug mode and APEX_ERROR for handling errors.
For your code that would be something like
DECLARE
--variable declare
BEGIN
-- Setting the Enviroment --
FOR I
IN (SELECT oh.header_id order_header_id
FROM oe_order_headers_all oh
WHERE oh.order_number IN (12301000013) AND oh.org_id = v_org_id)
LOOP
BEGIN
-- CANCEL HEADER --
v_header_rec.cancelled_flag := 'Y';
v_header_rec.change_reason := 'Not Provided';
apex_debug.info('Starting cancel of API');
-- CALLING THE API TO CANCEL AN ORDER --
OE_ORDER_PUB.PROCESS_ORDER (
x_header_rec => v_header_rec_out
--... using the parameter
);
END LOOP;
EXCEPTION
WHEN OTHERS
THEN
apex_error.add_error (
p_message => 'error in block. reason is :' || SQLERRM,
p_display_location => apex_error.c_inline_in_notification );
END;

Consuming rest API from Oracle PLSQL is returning ??? for arabic characters

I am trying to call a restful API from my Oracle procedure.
First, the API method is of type get and not post, so parameters are sent through header. the main purpose of the API is to send the received message as SMS to some providers and sometimes they are in Arabic format; We realized that Arabic received SMS are incomprehensible;
So I created a test procedure that takes a message and sends it to a test API method that returns the same message as response.
The API call succeeded but the response, only when arabic format is used, looks like ����. What should be added to my procedure so messages can be readable? I have tried to use escape for the message and to set the header format as you can see in below template, but unfortunately nothing succeeded:
PROCEDURE TEST(lang VARCHAR2,
message VARCHAR2,
P_RESPONSE OUT VARCHAR2) AS
v_request UTL_HTTP.req;
v_response UTL_HTTP.resp;
v_text VARCHAR2(1024);
v_url VARCHAR2(1024);
v_message VARCHAR2(1024);
l_webservice_link VARCHAR2(128);
BEGIN
BEGIN
P_RESPONSE := '';
v_message := utl_url.escape(message);
--v_message :=utl_url.escape(message,false,'UTF-8');
--v_message :=utl_url.escape(message,false,'windows-1256');
--v_message :=utl_url.escape(message,false,'AL32UTF8');
--v_message :=utl_url.escape(message,false,'AR8MSWIN1256');
l_webservice_link := GET_PARAM('REST_API_URL');
v_url := l_webservice_link ||
'Mytest?strMessage=' || v_message||
'&strLang=' || lang;
v_request := UTL_HTTP.begin_request(v_url);
--UTL_HTTP.set_header(v_request, 'Content-Type', 'charset=UTF-8');
--UTL_HTTP.set_header(v_request, 'Content-Type', 'windows-1256');
v_response := UTL_HTTP.get_response(v_request);
LOOP
BEGIN
UTL_HTTP.read_text(v_response, v_text);
DBMS_OUTPUT.put_line(v_text);
EXCEPTION
WHEN UTL_HTTP.end_of_body THEN
NULL;
END;
EXIT WHEN v_text IS NULL;
END LOOP;
UTL_HTTP.end_response(v_response);
IF v_response.status_code <> 200 THEN
P_RESPONSE := v_response.reason_phrase;
END IF;
EXCEPTION
WHEN OTHERS THEN
P_RESPONSE := 'An error has occured: ' || SQLERRM;
END;
END TEST;
Any help is more than appreciated.
Try to insert this code:
Charset VARCHAR2(20);
BEGIN
SELECT UTL_I18N.MAP_CHARSET(VALUE)
INTO Charset
FROM nls_database_parameters
WHERE parameter = 'NLS_CHARACTERSET';
UTL_HTTP.set_header(v_request, 'Content-Type', 'text/html; charset='||Charset);
I am not familiar with REST, I don't know wether text/html; is required and correct.
Update
I just see your database character set it AR8ASMO8X which does not have any IANA name (at least not according to Oracle UTL_I18N.MAP_CHARSET)
In this case try
UTL_HTTP.set_header(v_request, 'Content-Type', 'text/html; charset=UTF-8');
UTL_HTTP.begin_request(CONVERT(v_url,'AL32UTF8'));
Most likely the server returns response in UTF-8 - would be the most common one, otherwise check the header of the response.
Then try this:
UTL_HTTP.SET_BODY_CHARSET(v_response, 'AL32UTF8');
Apart from all above you may have also a display issue, i.e. inside Oracle everything would be fine, just your client is not able to display the characters properly, see OdbcConnection returning Chinese Characters as "?"

ORA-29263 thrown error ONLY when URL becomes LONG

I am trying to call a restful service from my ora pkg; Sometimes with the same code, this service is reached successfully and a response is returned; but when the message in the url becomes longer ==> ORA-29273 http request and ORA-29263 http protocol are thrown; I do not change anything between calls, only messages to be sent from this URL; Below is my procedure:
PROCEDURE SEND_SMS(username VARCHAR2,
password VARCHAR2,
phone VARCHAR2,
message VARCHAR2,
code VARCHAR2,
P_RESPONSE OUT VARCHAR2) AS
v_request UTL_HTTP.req;
v_response UTL_HTTP.resp;
v_text VARCHAR2(1024);
v_url VARCHAR2(1024);
v_message VARCHAR2(1024);
BEGIN
BEGIN
P_RESPONSE := '';
v_message := REPLACE(message,' ', '%20');
v_url := 'http://myservicelink.com/SendSMS?strUsername=' |
username || '&strPassword=' || password ||
'&strPhone=' || phone || '&strcode=' || code ||
'&strMessage=' || v_message;
v_request := UTL_HTTP.begin_request(v_url);
DBMS_OUTPUT.put_line(v_url);
v_response := UTL_HTTP.get_response(v_request);
LOOP
BEGIN
UTL_HTTP.read_text(v_response, v_text);
DBMS_OUTPUT.put_line(v_text);
EXCEPTION
WHEN UTL_HTTP.end_of_body THEN
NULL;
END;
EXIT WHEN v_text IS NULL;
END LOOP;
UTL_HTTP.end_response(v_response);
IF v_response.status_code <> 200 THEN
P_RESPONSE := v_response.reason_phrase;
END IF;
EXCEPTION
WHEN OTHERS THEN
P_RESPONSE := 'An error has occured: ' || SQLERRM;
END;
END SEND_SMS;
So when v_message becomes longer, exception is thrown. What can be the error; Please note that the same link is called from outside ORA pkg with same inputs --> myservice is reached;
Here is a template of my working message:
Dear X, you have been registered to our app. You can download the app for iPhone at link of app store or for android at link of
Not working:
Dear X, you have been registered to our app. You can download the app for iPhone at link of app store or for android at link of play store
your
The difference between your two strings isn't just length; the second one has a newline character in it as well. You are replacing spaces using:
v_message := REPLACE(message,' ', '%20');
but that won't deal with any other illegal characters.
If you change that to use the utl_url.escape() function:
v_message := utl_url.escape(message);
then it will escape the new line as well, so you end up with this for the first message (same as your current code gives):
Dear%20X,%20you%20have%20been%20registered%20to%20our%20app.%20You%20can%20download%20the%20app%20for%20iPhone%20at%20link%20of%20app%20store%20or%20for%20android%20at%20link%20of
but this for the second:
Dear%20X,%20you%20have%20been%20registered%20to%20our%20app.%20You%20can%20download%20the%20app%20for%20iPhone%20at%20link%20of%20app%20store%20or%20for%20android%20at%20link%20of%20play%20store%0Ayour
The newline has been encoded as %0A. Depending on what else is in the message, you may need to pass true as the second argument instead of letting it default to false; the difference is explained in the documentation link above.

Oracle, trigger to control link between tables

I need to create a trigger to control a link between tables. I have the following table structure (image below). an item is linked to a budget and a structure, and this structure should be linked to the same budget.
Sometimes the item receive wrong structures, receive structures different from its own budget budget. I need that when you insert a budget item or edit a budget item to be validated if the id_budget are equal, the id_budget of dg_budget_item is equal to the id_budget dg_budget_structure to which it is being linked
I started building this trigger, but do not know how to continue
CREATE TRIGGER T_BUDGETO_STRUCTURE_ITEM_ID_STRUCTURE
BEFORE INSERT OR UPDATE OF id_budget_structure ON dg_budget_structure_item
select id_budget from dg_budget_structure where id_budget_structure = new.id_budget_structure
FOR EACH ROW
WHEN (new.id_budget <> ???(result of select?))
pl/sql_block
This should get you close to what you want
CREATE OR REPLACE TRIGGER T_BUDGET_STRUCTURE_ITEM_ID_STRUCTURE
BEFORE INSERT OR UPDATE OF id_budget_structure ON dg_budget_item
FOR EACH ROW
DECLARE
v_id_budget_structure NUMBER;
Budgets_do_not_match EXCEPTION;
v_code NUMBER;
v_errm VARCHAR2(64);
BEGIN
SELECT id_budget INTO v_id_budget_structure FROM dg_budget_structure WHERE id_budget_structure = :new.id_budget_structure
IF :new.id_budget <> v_id_budget_structure THEN
RAISE Budgets_do_not_match;
END IF;
EXCEPTION
WHEN Budgets_do_not_match THEN
Raise_application_error (-20001,
'Budget '||TO_CHAR(:new.id_budget)||' for structure '
|| :new.id_budget_structure || 'does not match the budget trying to be linked';
WHEN NO_DATA_FOUND THEN
Raise_application_error(-20002,
'Invalid budget structure ' ||:new.id_budget_structure);
WHEN OTHERS THEN
v_code := SQLCODE;
v_errm := SUBSTR(SQLERRM, 1 , 64);
Raise_application_error(-20000,
'Unexpected error ' || v_code ': ' || v_errm;
END;
You cannot ROLLBACK within a trigger so your application will need to deal with that.

Oracle DB: Email Trigger

I am writing a trigger for an Oracle DB to send an email then update a column for that record acknowledging that the email has been sent. I was advised to create a cursor to fetch each row, then gather the info for the email, send the email, update the record, then repeat in a loop. The code below is what I have so far.
CREATE OR REPLACE TRIGGER "SEND_EMAIL"
After INSERT OR UPDATE OF ISSUE_ADDED_TO_ALM ON DB_TABLE FOR EACH ROW
DECLARE
l_table DB_TABLE%rowtype;
l_body varchar2(4000);
l_to_address varchar2(2000);
l_from varchar2(200);
l_name varchar2(100);
l_summary varchar2(1000);
l_description varchar2(4000);
l_ALM_ID varchar2(100);
l_subject varchar2(400);
l_added_to_alm varchar2(200);
l_SID varchar2(200);
CURSOR cur_ADDED_TO_ALM IS
select * FROM DB_TABLE where ISSUE_ADDED_TO_ALM = '1' and EMAIL_NOTIFICATION = '0';
BEGIN
OPEN cur_ADDED_TO_ALM;
LOOP
Fetch cur_ADDED_TO_ALM into l_table;
Exit when cur_ADDED_TO_ALM%NOTFOUND;
l_from := 'Data Quality IMS Team';
select ISSUE_REQUESTER into l_SID from DB_TABLE;
select emp_email_name,concat(concat(emp_first_name,' '),emp_last_name) into l_to_address, l_name from telephone_book where emp_id = l_SID;
select ISSUE_SUMMARY,ISSUE_DESCRIPTION,ALM_ISSUE_ID into l_summary, l_description, l_ALM_ID from DB_TABLE where ISSUE_ADDED_TO_ALM = '1' and EMAIL_NOTIFICATION = '0';
l_subject := l_ALM_ID + 'Request has been created.';
l_body := '<style type="text/css">
p{font-family: Calibri, Arial, Helvetica, sans-serif;
font-size:12pt;
margin-left:30px;
}
</style>';
l_body := l_body || '<p>Your request has been created.</p>';
l_body := l_body || '<p>Data Quality Center received the following request:</p>';
l_body := l_body || '<p>Request ID: '|| l_ALM_ID ||'</p>';
l_body := l_body || '<p>Request Title: '|| l_summary||'</p>';
l_body := l_body || '<p>Request Description: '|| l_description||'</p>';
HTMLDB_MAIL.SEND(
P_TO => l_to_address,
P_FROM => l_from,
P_BODY => l_body,
P_BODY_HTML => l_body,
P_SUBJ => l_subject);
wwv_flow_mail.push_queue(
P_SMTP_HOSTNAME => 'mail.sever_name.net',
P_SMTP_PORTNO => '5'
);
END LOOP;
update DB_TABLE set EMAIL_NOTIFICATION = '1' where ALM_ISSUE_ID = l_ALM_ID
IF cur_ADDED_TO_ALM%ISOPEN then
CLOSE cur_ADDED_TO_ALM;
END IF;
end;
I get the following error:
ORA-04091: table server.DB_Table is mutating, trigger/function may not see it ORA-06512: at "server.SEND_EMAIL", line 15 ORA-06512: at "server.SEND_EMAIL", line 18 ORA-04088: error during execution of trigger 'server.SEND_EMAIL'
I offer these solutions.
You cannot UPDATE the table upon which the trigger is activating. You should instead set the value using :new.EMAIL_NOTIFICATION := 1;
You cannot SELECT the table upon which the trigger is activating. This logically doesn't make sense. That's the reason you're getting a "mutating table" error. The table is changing (mutatiing) and you want to read it, which would produce ambiguous results. DO NOT GO AROUND THIS WITH AN AUTONOMOUS TRANSACTION. Someone somewhere is going to give you this suggestion. That would be digging yourself even deeper into a hole.
There is no need for the explicit cursor. Use an implicit one for clarity and simplicity.
Overall, this is a terrible idea. For a purpose such as this you really don't want to do it this way. Think about it, the user does an insert or update and must wait until the database sends all the necessary emails. If there are any exceptions raised in the trigger the transaction fails and you have to roll back.
Better plans
Have the trigger place the email requests into a table. Have a job that runs frequently to send these emails to the email server in the background so the user doesn't wait.
Have the trigger place email-like messages into a queue. Have a job that ....
Avoid the trigger completely. Just have a job which identifies these records and ....

Resources