UTL_HTTP call inside trigger leads to ORA-06502 when inserting from select
Frage
In order to allow a developer to make a SOAP call from an Oracle 11gR1 (11.1.0.7 PSU 24) instance using UTL_HTTP.
He created a stored procedure :
CREATE OR REPLACE PROCEDURE CALL_WS (URL IN VARCHAR2, REQUEST IN VARCHAR2,
RESPONSE OUT VARCHAR2, ERRLOG OUT VARCHAR2) IS
L_HTTP_REQ UTL_HTTP.REQ;
L_HTTP_RESP UTL_HTTP.RESP;
L_HTTP_URL_V VARCHAR2 (32767);
IP_INPUTDATA_V VARCHAR2 (32767);
L_OUTPUTDATA_V VARCHAR2 (32767);
BEGIN
L_HTTP_URL_V := URL;
IP_INPUTDATA_V := REQUEST;
ERRLOG := NULL;
-- Configuration
UTL_HTTP.SET_DETAILED_EXCP_SUPPORT (TRUE);
L_HTTP_REQ := UTL_HTTP.BEGIN_REQUEST (L_HTTP_URL_V, 'POST', 'HTTP/1.1');
-- Authentification (not necessary at the moment)
--UTL_HTTP.SET_AUTHENTICATION (L_HTTP_REQ, L_HTTP_USERNAME_V, L_HTTP_PASSWORD_V);
UTL_HTTP.SET_PERSISTENT_CONN_SUPPORT (L_HTTP_REQ, FALSE);
-- Headers
UTL_HTTP.SET_HEADER (L_HTTP_REQ, 'Content-Type', 'text/xml');
UTL_HTTP.SET_HEADER (L_HTTP_REQ, 'charset', 'UTF-8');
UTL_HTTP.SET_HEADER (L_HTTP_REQ, 'Content-Length', LENGTH (IP_INPUTDATA_V));
--WRITES SOME TEXT DATA IN THE HTTP REQUEST BODY
UTL_HTTP.WRITE_TEXT (L_HTTP_REQ, IP_INPUTDATA_V);
-- GET HTTP RESPONSE
L_HTTP_RESP := UTL_HTTP.GET_RESPONSE (L_HTTP_REQ);
-- GET THE RESPONSE TEXT VALUE
UTL_HTTP.READ_TEXT (L_HTTP_RESP, L_OUTPUTDATA_V);
-- Check HTTP status code
IF (L_HTTP_RESP.STATUS_CODE <> 200) THEN
ERRLOG := 'HTTP REQUEST CALL FAILED. STATUS CODE IS ' || L_HTTP_RESP.STATUS_CODE;
END IF;
UTL_HTTP.END_RESPONSE (L_HTTP_RESP);
-- Set Output parameter
RESPONSE := L_OUTPUTDATA_V;
EXCEPTION
WHEN OTHERS THEN
RESPONSE := NULL;
ERRLOG := SQLERRM;
END;
And a BEFORE INSERT trigger as follows:
CREATE OR REPLACE TRIGGER TR_kkk_AFTERIN
BEFORE INSERT OR UPDATE ON CHU_kkk
REFERENCING NEW AS N OLD AS O
FOR EACH ROW
DECLARE
-- Variable declarations
l_URL VARCHAR2 (32767);
l_REQUEST VARCHAR2 (32767);
l_RESPONSE VARCHAR2 (32767);
l_ERRLOG VARCHAR2 (32767);
v_def definitions.compterendu%TYPE;
BEGIN
IF :n.traite = 0 THEN
-- Variable initializations
l_URL := 'http://zz/uu/services.asmx';
v_def := NULL;
-- Get request SOAP model in a Omnipro definition
SELECT VALUE INTO v_def
FROM definitions
WHERE cle LIKE 'kkk';
IF v_def IS NOT NULL THEN
-- Replace values in the request SOAP
l_REQUEST := REPLACE(v_def, '@@DPTID@@', :n.dptid);
l_REQUEST := REPLACE(l_REQUEST, '@@USERID@@', :n.userid);
l_REQUEST := REPLACE(l_REQUEST, '@@APPTID@@', :n.apptid);
l_REQUEST := REPLACE(l_REQUEST, '@@REFID@@', :n.refid);
l_REQUEST := REPLACE(l_REQUEST, '@@REFAPP@@', :n.refapp);
l_REQUEST := REPLACE(l_REQUEST, '@@DTEDEB@@', TO_CHAR(:n.dtedeb, 'yyyy-mm-dd'));
l_REQUEST := REPLACE(l_REQUEST, '@@DTEFIN@@', TO_CHAR(:n.dtefin, 'yyyy-mm-dd'));
l_REQUEST := REPLACE(l_REQUEST, '@@NUMNAT@@', :n.numnat);
l_REQUEST := REPLACE(l_REQUEST, '@@NUMREF@@', :n.numref);
-- Call
CALL_WEB_SERVICE (URL => l_URL,
REQUEST => l_REQUEST,
RESPONSE => l_RESPONSE,
ERRLOG => l_ERRLOG);
IF l_ERRLOG IS NOT NULL THEN
:n.traite := 9;
:n.reflog := l_ERRLOG || ' : ' || SUBSTR (l_RESPONSE, 0, 1900);
ELSE
:n.traite := 1;
END IF;
:n.dtetrt := SYSDATE;
END IF;
END IF;
EXCEPTION
WHEN OTHERS THEN
-- Consider logging the error and then re-raise
:n.traite := 9;
:n.reflog := SQLERRM;
:n.dtetrt := SYSDATE;
--RAISE;
END;
Everything works well for an INSERT query with a single value, but fails whenever he calls:
INSERT INTO ... VALUES ...
COMMIT;
INSERT INTO ... VALUES ...
COMMIT;
He gets an ORA-06502.
If he calls each insert separately, there is no error...
I suggested him to investigate using SQLDeveloper debugger, but we are not sure that it will lead to something...
What could cause that ?
Keine korrekte Lösung
Andere Tipps
You should use proper data types, i.e. CLOB
. For the SOAP request I would recommend XMLTYPE
.
I have a similar use case and I use this code to generate the request:
CREATE OR REPLACE TYPE VARCHAR_TABLE_TYPE AS TABLE OF VARCHAR2(1000);
FUNCTION GenerateRequest(nodes IN VARCHAR_TABLE_TYPE) RETURN XMLTYPE IS
res XMLTYPE;
BEGIN
SELECT
XMLELEMENT("soapenv:Envelope",
XMLATTRIBUTES(
'http://schemas.xmlsoap.org/soap/envelope/' AS "xmlns:soapenv",
'http://xxx.yyy.zzz/' AS "xmlns:ser"),
XMLELEMENT("soapenv:Header"),
XMLELEMENT("soapenv:Body",
XMLELEMENT("ser:getAffectedServices",
XMLELEMENT("arg0",
XMLELEMENT("appName", 'com'),
XMLELEMENT("asmTicket", 'dummy'),
XMLELEMENT("principal", 'dummy')
),
XMLELEMENT("arg1", 1),
XMLAGG(XMLELEMENT("arg2", COLUMN_VALUE)),
XMLELEMENT("arg3", 'basic')
)
)
)
INTO res
FROM TABLE(nodes);
RETURN res;
END GenerateRequest;
And this is the HTTP call. Both, request and response message are defined as CLOB/XMLTYPE
:
PROCEDURE GetServices(node IN VARCHAR_TABLE_TYPE) IS
l_http_request UTL_HTTP.REQ;
l_http_response UTL_HTTP.RESP;
BUFFER VARCHAR2(32767);
l_response_clob CLOB;
p_soap_msg CLOB;
msg_length INTEGER;
p_url VARCHAR2(4000) := 'http://zz/uu/services.asmx'
amount INTEGER := 10000;
OFFSET PLS_INTEGER := 1;
BEGIN
p_soap_msg := GenerateRequest(node).GetClobVal();
msg_length := DBMS_LOB.GETLENGTH(p_soap_msg);
l_http_request := UTL_HTTP.BEGIN_REQUEST(p_url, 'POST', UTL_HTTP.HTTP_VERSION_1_1);
UTL_HTTP.SET_HEADER(l_http_request, 'Content-Type', 'text/xml; charset="utf-8"');
UTL_HTTP.SET_HEADER(l_http_request, 'Content-Length', msg_length);
LOOP
EXIT WHEN OFFSET > msg_length;
DBMS_LOB.READ(p_soap_msg, amount, OFFSET, BUFFER);
UTL_HTTP.WRITE_TEXT(l_http_request, BUFFER);
OFFSET := OFFSET + amount;
END LOOP;
l_http_response := UTL_HTTP.GET_RESPONSE(l_http_request);
DBMS_LOB.CREATETEMPORARY(l_response_clob, TRUE, DBMS_LOB.CALL);
DBMS_LOB.OPEN(l_response_clob, DBMS_LOB.LOB_READWRITE);
BEGIN
LOOP
UTL_HTTP.READ_TEXT(l_http_response, BUFFER, 8192);
DBMS_LOB.WRITEAPPEND(l_response_clob, LENGTH(BUFFER), BUFFER);
END LOOP;
EXCEPTION
WHEN UTL_HTTP.END_OF_BODY THEN
UTL_HTTP.END_RESPONSE(l_http_response);
END;
DBMS_LOB.CLOSE(l_response_clob);
INSERT INTO T_TEMP_SOAP_RES VALUES (XMLTYPE(l_response_clob));
COMMIT;
DBMS_LOB.FREETEMPORARY(l_response_clob);
EXCEPTION
WHEN OTHERS THEN
Logging.put('Error while getting Service', ErrCode => SQLCODE, ErrMessage => SQLERRM ||CHR(13)|| SUBSTR(l_response_clob, 1, 900));
RAISE;
END GetServices;