Reputation: 1156
I have a function in SQL that inserts a user into USERS. When I call the function in Oracle 12c application express using SELECT insert_users('user', 'email', 'hash') FROM dual;
it runs, but returns FAIL
from the exception. The same thing happens in SQL Developer when running this SQL statement, however when I run or debug the function directly in SQL Developer, it successfully executes. So I am thinking that the issue is in the SQL statement. So what needs to change to get it working?
SQL Function
create or replace FUNCTION insert_users(p_user_name in varchar2,
p_user_email in varchar2, p_user_password in varchar2)
RETURN VARCHAR2 AS
p_salt varchar2(20) := '';
BEGIN
select dbms_random.string('P', 20) str
into p_salt
from dual;
INSERT INTO USERS(USER_ID, USER_NAME, USER_EMAIL, SALT, USER_PASSWORD)
VALUES (seq_users.nextval, p_user_name, p_user_email, p_salt, p_user_password);
return 'SUCCESS';
EXCEPTION
WHEN others THEN
RETURN 'FAIL';
END;
SQL Call
SELECT insert_users('user', 'email', 'hash') FROM dual;
PL/SQL Block (from running function directly)
DECLARE
P_USER_NAME VARCHAR2(200);
P_USER_EMAIL VARCHAR2(200);
P_USER_PASSWORD VARCHAR2(200);
v_Return VARCHAR2(200);
BEGIN
P_USER_NAME := 'user';
P_USER_EMAIL := 'email';
P_USER_PASSWORD := 'hash';
v_Return := USER.INSERT_USERS(
P_USER_NAME => P_USER_NAME,
P_USER_EMAIL => P_USER_EMAIL,
P_USER_PASSWORD => P_USER_PASSWORD
);
:v_Return := v_Return;
--rollback;
END;
Upvotes: 0
Views: 1108
Reputation: 191235
If you weren't squashing the exception you'd see:
ORA-14551: cannot perform a DML operation inside a query
Your function is doing DML - i.e. inserting into your table. When run from a PL/SQL context that's OK, though generally it's considered better to use a procedure for that anyway. But since your function is doing that, you cannot call it as part of a query.
Catching and squashing errors is often considered a bug. All you are doing it hiding useful information. The caller has no idea why the function call failed, and anyone investigating the problem has no idea what is going on either.
A better approach would be to just have a procedure. If the insert works that's fine. If any kind of error occurs let the exception propogate, and the client or caller will see it and know what actually went wrong. You can't call the procedure from a query, but you can call it from an anonymous block, or from the SQL*Plus and SQL Developer execute
wrapper around an anonymous block.
create or replace PROCEDURE insert_users(p_user_name in varchar2,
p_user_email in varchar2, p_user_password in varchar2) AS
BEGIN
INSERT INTO USERS(USER_ID, USER_NAME, USER_EMAIL, SALT, USER_PASSWORD)
VALUES (seq_users.nextval, p_user_name, p_user_email, dbms_random.string('P', 20), p_user_password);
END;
/
Procedure INSERT_USERS compiled
EXEC insert_users('user', 'email', 'hash');
PL/SQL procedure successfully completed.
select * from users;
USER_ID USER_NAME USER_EMAIL SALT USER_PASSWORD
---------- -------------------- -------------------- -------------------- ----------------------------------------------------------------
1 user email Er-U1zL-v0lP%1m*Tz&t hash
You don't really need the p_salt
variable, you can call dbms_random
as part of the insert, so I've removed that.
You probably want to hash the password using the salt before storing it though...
Upvotes: 3