Triggers do it because they are taking advantage of the special case when the lob is being built from scratch. To demonstrate what I mean:
SQL> drop table T purge;
Table dropped.
SQL>
SQL> create table T ( x clob, y clob );
Table created.
SQL>
SQL> create or replace
2 trigger TRG before insert or update on T
3 for each row
4 begin
5 if :new.x != :new.y then
6 raise_application_error(-20000,'error');
7 end if;
8 end;
9 /
Trigger created.
SQL>
SQL> insert into T values ('qwe','qwe');
1 row created.
SQL> update T set y = 'abc';
update T set y = 'abc'
*
ERROR at line 1:
ORA-20000: error
ORA-06512: at "MCDONAC.TRG", line 3
ORA-04088: error during execution of trigger 'MCDONAC.TRG'
So all looks good right ? But then look at this
SQL>
SQL> declare
2 c clob;
3 begin
4 select y into c from t for update;
5 dbms_lob.writeappend(c,100,rpad('x',100,'x'));
6 commit;
7 end;
8 /
PL/SQL procedure successfully completed.
SQL>
SQL> select * from T;
X
--------------------------------------------------------------------------------
Y
--------------------------------------------------------------------------------
qwe
qwexxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
SQL>
SQL>
So, yes, you're correct - the way we deal with clobs in triggers is different to simply equality in SQL.
DBMS_LOB.COMPARE will work but is very resource intensive for large clobs. If this is a need, better to calculate a checksum as you store the clob and use that.