简体   繁体   中英

Oracle 12c SQL Function call not working as desired

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;

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 - ie 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...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM