One of the very cool things we’ve had for many years in the Oracle database is the ability to capture values after DML without having to do another trip back to the database. We do this using the returning clause. I’ll whip up a very simple example in SQL plus using SQL plus variables, but this works in any coding language that has an interface to the Oracle database.

SQL> desc myemp
Name Null? Type
----------------------------- -------- --------------------
EMPNO NUMBER(38)
ENAME VARCHAR2(10)
SQL> variable which_emp number;
SQL>
SQL> update myemp
2 set ename = 'CONNOR'
3 where ename = 'MARY'
4 returning empno into :which_emp;
1 row updated.

That all seems simple enough, but sometimes you might stumble across the following error which seems to suggest something catastrophically wrong has happened with the way you are using bind variables.

SQL> variable which_emp number;
SQL>
SQL> update myemp
2 set ename = 'CONNOR'
3 where ename = 'PETE'
4 returning empno into :which_emp;
update myemp
*
ERROR at line 1:
ORA-24369: required callbacks not registered for one or more bind handles

Callbacks? Registrations? If I jump onto chatGPT, it gives me some very cryptic responses about what the cause might be, which is what motivated me to throw this blog post together to set you straight 🙂

I’m sure all of the above is probably true, but it is much more likely to be something else. The cause is actually very simple. When we are using RETURNING we are expecting one row to be returned into our RETURNING variable value, but you’ll get the error if the database picked up more than one row. If I query my MYEMP table, you can see that there were actually two rows for the employee name of “PETE”.

SQL> select * from myemp;
EMPNO ENAME
---------- ----------
1 JOHN
2 SUE
3 MARY
4 JANE
5 PETE
6 PETE
6 rows selected.

Be aware that even though we could not return the values, we did indeed go ahead and perform the update. The rows above were shown before I did the update. If I run that update then even with its error, you can see I did indeed modify rows for EMPNO 5 and 6.


SQL> select * from myemp;

     EMPNO ENAME
---------- ----------
         1 JOHN
         2 SUE
         3 CONNOR
         4 JANE
         5 CONNOR
         6 CONNOR

6 rows selected.

So if you get this error in your application code, you probably want to roll back or roll back to a save point. Assuming that it’s a significant problem if you no longer have access to the returned variable values.

As a quick aside, one of the very cool things that has come in 26ai is the ability to distinguish between the before-value of a column versus the updated value in your RETURNING clause. You can now add the OLD and NEW keywords to specify which version of the row image you would like. By default, we return the new version to retain backward compatibility with the original returning clause.

SQL> create table myemp ( empno int, ename varchar2(10), sal int);
Table created.
SQL> insert into myemp values (1,'JOHN',1000);
1 row created.
SQL> insert into myemp values (2,'SUE',2000);
1 row created.
SQL> insert into myemp values (3,'MARY',3000);
1 row created.
SQL> insert into myemp values (4,'JANE',4000);
1 row created.
SQL> insert into myemp values (5,'PETE',5000);
1 row created.
SQL> insert into myemp values (6,'PETE',6000);
1 row created.
SQL> commit;
Commit complete.
SQL>
SQL> variable prev_name varchar2(30)
SQL> variable new_sal number
SQL>
SQL> update myemp
2 set ename = 'CONNOR',
3 sal = sal*1.1
4 where empno = 3
5 returning old ename, new sal
6 into :prev_name, :new_sal;
1 row updated.
SQL>
SQL>
SQL>
SQL>
SQL> print prev_name
PREV_NAME
-------------------------------
MARY
SQL> print new_sal
NEW_SAL
----------
3300

Happy binding!

One response to “ORA-24369? Required callbacks? Broken binds?”

  1. Hi Connor,

    I had a cold chill running when I saw this … but, just discovered how addicted I am to PL/SQL,
    where I checked this scenario many times.

    And, indeed, PL/SQL behaves differently, it raises a TOO_MANY_ROWS exception, as expected, and the UPDATE is rolled back,
    even though the RETURNING variable is populated with the first value, but, as by documentation, in case of such an error
    the target variables values should not be relied upon ( just like for SELECT … INTO … ).

    DECLARE
    l_which_emp number;
    BEGIN
    update myemp
    set ename = ‘CONNOR’
    where ename = ‘PETE’
    returning empno into l_which_emp ;
    EXCEPTION
    WHEN OTHERS THEN
    DBMS_OUTPUT.put_line(SQLERRM);
    DBMS_OUTPUT.put_line(‘l_which_emp=’||l_which_emp);
    —
    for r in (select * from myemp order by empno)
    loop
    dbms_output.put_line(r.empno||’ – ‘||rpad(r.ename,10));
    end loop;
    END;
    /

    ORA-01422: exact fetch returned more than the requested number of rows DML Returning: Too many rows
    l_which_emp=5
    1 – JOHN
    2 – SUE
    3 – MARY
    4 – JANE
    5 – PETE
    6 – PETE

    I really don’t understand why was it needed “to reinvent the wheel” for an old and well-known behavior ?!?

    Cheers & Best Regards,
    Iudith

Leave a Reply

Trending