Karl about the Oracle Database

Some experiences out of my daily oracle practice

PL/SQL : Bypassing code due to deferred constraints

Posted by carl.reitschuster on June 19th, 2006

Hi reader,

do you know how deferred constraints work? No problem : The constraint is not checked after statement completion but on COMMIT of the transaction. Simple i thought. But a deferred constraint could have effect on your code. I created a package for the handling of CUSTOMER structures. A root node is always needed if a CUSTOMER of a preferred pool is created or updated. A CUSTOMER may only appear once in the structure and due to application logic the Unique key on the CUSTOMER is checked deferred.


– Create/Recreate primary, unique and foreign key constraints
alter table CUSTOMERSTRUCTS
 
add constraint CUSTOMERSTRUCTS_UK01 unique (CUSTOMERID) INITIALLY DEFERRED DEFERRABLE
 
using index
 
tablespace CUSTIXM
;

The procedure simple ignores the UNIQUE key vailation due to an insert of a companies root node if it already exists :

PROCEDURE Create_Root_Node(p_CUSTOMER IN Guid) IS
 
BEGIN
 
   
INSERT INTO CUSTOMERstructs
     
(Id,

       CUSTOMERid
,
      
PARENT,
       Lastupdate
)
   
VALUES
     
(Sys_Guid(),
       p_CUSTOMER
,
      
NULL,
      
SYSDATE);
 
 
EXCEPTION
   
WHEN Dup_Val_On_Index THEN
     
NULL; – do nothing
   
WHEN OTHERS THEN
      Eventlogmgr.Logapperror
(SQLCODE,
                             
‘Error creating CUSTOMERstructs Root Node’,
                             
‘CUSTOMERstructs_Util’,
                             
‘Create_Root_Node’,
                              
‘INSERT INTO CUSTOMERSTRUCTS’);
   
     
RAISE;
 
END Create_Root_Node;

Now the test :

SQL>
SQL> DECLARE
  2    l_CUSTOMER_Id CUSTOMER.Id%TYPE;
  3  BEGIN
  4 
  5    SELECT Id
  6      INTO l_CUSTOMER_Id
  7      FROM CUSTOMER
  8 
  9     WHERE Rownum < 2
 10       AND Isis_Prefered = 1;
 11 
 12    — Ignore already inserted CUSTOMER node.
 13    CUSTOMERstructs_Util.Create_Root_Node(p_CUSTOMER => l_CUSTOMER_Id);
 14    CUSTOMERstructs_Util.Create_Root_Node(p_CUSTOMER => l_CUSTOMER_Id);
 15    CUSTOMERstructs_Util.Create_Root_Node(p_CUSTOMER => l_CUSTOMER_Id);
 16 
 17  END;
 18  /

PL/SQL procedure successfully completed

SQL>

Seems to work! Finally the commit :

SQL> COMMIT;

COMMIT

ORA-02091: Transaktion wurde zurückgesetzt
ORA-00001: Unique Constraint (ISIS.CUSTOMERSTRUCTS_UK01) verletzt

SQL>

Uups! What happened here? The part of the Code …

EXCEPTION
   
WHEN Dup_Val_On_Index THEN
     
NULL;– do nothing

… was never executed because of the deferred constraint. On the time of Commit the UNIQUE constraint was hurt. Following Code worked properly with own implemented unique key lookup:

PROCEDURE Create_Root_Node(p_CUSTOMER IN Guid) IS
  l_Num
INTEGER;
BEGIN
 
– manual lookup because of deferred unique constraint
 
SELECT 1
   
INTO l_Num
   
FROM
CUSTOMERstructs Cs
  
WHERE Cs.CUSTOMERid = p_CUSTOMER;

EXCEPTION
 
WHEN No_Data_Found THEN
   
BEGIN
     
INSERT INTO CUSTOMERstructs
       
(Id,

         CUSTOMERid
,
        
PARENT,
         Lastupdate
)
     
VALUES
       
(Sys_Guid(),
         p_CUSTOMER
,
        
NULL,
        
SYSDATE);
   
EXCEPTION
     
WHEN OTHERS THEN
        Eventlogmgr.Logapperror
(SQLCODE,
                               
‘Error creating CUSTOMERstructs Root Node’,
                               
‘CUSTOMERstructs_Util’,
                               
‘Create_Root_Node’,
                               
‘INSERT INTO CUSTOMERstructs’);
       
RAISE;
   
END;
END Create_Root_Node;

and it did …

SQL>
SQL> DECLARE
  2    l_CUSTOMER_Id CUSTOMER.Id%TYPE;
  3  BEGIN
  4 
  5    SELECT Id
  6      INTO l_CUSTOMER_Id
  7      FROM CUSTOMER
  8 
  9     WHERE Rownum < 2
 10       AND Isis_Prefered = 1;
 11 
 12    — Ignore already inserted CUSTOMER node.
 13    CUSTOMERstructs_Util.Create_Root_Node(p_CUSTOMER => l_CUSTOMER_Id);
 14    CUSTOMERstructs_Util.Create_Root_Node(p_CUSTOMER => l_CUSTOMER_Id);
 15    CUSTOMERstructs_Util.Create_Root_Node(p_CUSTOMER => l_CUSTOMER_Id);
 16 
 17  END;
 18  /

PL/SQL procedure successfully completed

SQL> COMMIT;

Commit complete

Conclusion

changing a Constraint to deferred could bypass some code fragments in your application which rely on immediate check of the constraints. Having this in mind deferred constraints are a comfortable way to change dependent data.

HTH

Karl

 

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>