CREATE OR REPLACE FUNCTION F5
(tstg IN stage.numerostage%type)
return sys-ryfcursor
DECLARE
BEGIN
OPEN list for
SELECT count(*) as nbrmodule,sess.*
from session sess natural join enseigne
where numerostage=tstg;
RETURN list;
END;
I get this error:
encountered the symbol
There are multiple errors in your function.
sys-ryfcursor
It's SYS_REFCURSOR.
DECLARE
You don't need the DECLARE keyword. Remove it.
A suggestion, always compile your code and use SHOW ERRORS in SQL*Plus or a similar client to see the full error stack.
For example,
SQL> CREATE OR REPLACE
2 FUNCTION F5(
3 tstg IN stage.numerostage%type)
4 RETURN SYS-ryfcursor
5 DECLARE
6 BEGIN
7 OPEN list FOR SELECT COUNT(*)
8 AS
9 nbrmodule,sess.* FROM session sess NATURAL JOIN enseigne WHERE numerostage=tstg;
10 RETURN list;
11 END;
12 /
Warning: Function created with compilation errors.
SQL> sho err
Errors for FUNCTION F5:
LINE/COL ERROR
-------- -----------------------------------------------------------------
3/15 PLS-00103: Encountered the symbol "-" when expecting one of the
following:
. # % ; is authid as cluster order using external character
deterministic parallel_enable pipelined aggregate
result_cache accessible
SQL>
Related
I trying to make a function with a parameter in PL/SQL but getting following error: PLS-00103: Encountered the symbol "SELECT". Here is my code:
CREATE OR REPLACE FUNCTION select_by_id(id_in IN NUMBER) RETURN clients%ROWTYPE
AS
result clients%ROWTYPE;
BEGIN
SELECT * INTO result FROM clients WHERE client_id = id_in;
RETURN result;
END;
SELECT select_by_id(2) FROM DUAL;
What is the cause of the error, and how can the problem be fixed?
Your function works (for a certain degree of works) if you terminate it with / on a newline.
If you have the table:
CREATE TABLE clients (client_id, other) AS
SELECT 2, 'XYZ' FROM DUAL;
Then the function (terminated by a / on a newline):
CREATE OR REPLACE FUNCTION select_by_id(
id_in IN NUMBER
) RETURN clients%ROWTYPE
AS
result clients%ROWTYPE;
BEGIN
SELECT * INTO result FROM clients WHERE client_id = id_in;
RETURN result;
END;
/
Compiles and can be called from PL/SQL:
DECLARE
client CLIENTS%ROWTYPE := select_by_id(2);
BEGIN
DBMS_OUTPUT.PUT_LINE(client.client_id || ', ' || client.other);
END;
/
and outputs:
2, XYZ
However, a %ROWTYPE is a PL/SQL record type and cannot be used in an SQL statement so:
SELECT select_by_id(2) FROM DUAL;
Will not work because the data type is invalid in the SQL scope.
fiddle
This is the function which I designed. It got complied successfully
CREATE OR REPLACE FUNCTION "F_CHECK"
(
p_id IN VARCHAR2
p_name IN VARCHAR2)RETURN VARCHAR2
is
v_id VARCHAR2;
v_name VARCHAR2;
cnt pls_integer;
BEGIN
IF id IS NULL THEN
RETURN NULL;
END IF;
SELECT COUNT(*) INTO cnt from emp_new where id = p_id;
IF (cnt > 0) THEN
SELECT id, name INTO v_id, v_name from emp_new where id=p_id;
IF (v_id is null and p_id is null and v_name is null and p_name is null) THEN
return NULL;
ELSE
IF (v_name =trunc(p_name)) then
return NULL;
else
insert into employees values(p_id,p_name,sysdate);
end if;
end if;
end if;
exception
when DUP_VAL_ON_INDEX THEN
raise application error (-20001, 'NAME EXISTS UNDER DIFFERENT ID');
END F_CHECK;
But I'm not getting the expected result, when I execute the function
select F_CHECK(1,1) from dual;
ERROR I'M getting is:
SQL EEROR: ORA-06503: PL/SQL : FUNCTION RETURNED WITHOUT VALUE
You must return a value when the execution flow reaches (around line 22)
...
else
insert into employees values(p_id,p_name,sysdate);
end if;
...
In this case, the function does not return a value which is expected by the caller, hence the error.
You can instruct the PL/SQL compiler to warn you about such code (and other problems) with
ALTER SESSION SET PLSQL_WARNINGS='ENABLE:ALL'; prior to compilation.
One of the possible causes of exceptions that you will get when you run this code is: If your select into didn't return a value, you will get an exception, an nu-handled exception
Even though you have a return NULL in the end of the function, but still you need to catch all the exceptions that might occur
The area where you need to take care of is:
SELECT id, name INTO v_id, v_name from emp_new where id=p_id;
Surround it by Begin ... EXCEPTION WHEN NO_DATA_FOUND THEN ... END; block
Also, your insert statement might cause an exception if you violated some constraints on your table, so you might need to handle that also
Have a look at Error Handling in Oracle
Below is your updated code, also fixed code regarding the extra parenthesis
Edited
CREATE OR REPLACE FUNCTION F_CHECK
(
P_ID EMP_NEW.ID%TYPE,
P_NAME EMP_NEW.NAME%TYPE
)
RETURN VARCHAR2 IS
V_ID EMP_NEW.ID%TYPE;
V_NAME EMP_NEW.NAME%TYPE;
CNT NUMBER;
BEGIN
--IF ID IS NULL THEN
-- What is ID ?? is it P_ID
--Changed below
IF P_ID IS NULL THEN
RETURN 'Error: Add Value For Id';
END IF;
IF P_NAME IS NULL THEN
RETURN 'Error: Add Value For Name';
END IF;
SELECT
COUNT(*) INTO CNT FROM EMPLOYEES
WHERE ID = P_ID;
IF (CNT > 0) THEN
SELECT
ID, NAME INTO V_ID, V_NAME
FROM
EMP_NEW
WHERE
ID=P_ID;
----------------------------------------------------------------------------------------
--IF V_ID IS NULL AND P_ID IS NULL AND V_NAME IS NULL AND P_NAME IS NULL THEN
--The code above will always evaluate to False because P_ID at this stage is not null!
--Also, if P_Name must have a value, check it at the begining along with the ID, not here
----------------------------------------------------------------------------------------
IF V_ID IS NULL AND V_NAME IS NULL THEN
RETURN 'Error: Not found details';
ELSE
--Details are found
IF (V_NAME = TRUNC(P_NAME)) THEN
RETURN 'Name already assigned to this id';
ELSE --Its a new name, add it
INSERT INTO EMPLOYEES VALUES(P_ID,P_NAME,SYSDATE);
--Make sure your columns are only three and in the correct order as the Values specified
END IF;
END IF;
END IF;
RETURN 'Ok, added';
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
raise application error (-20001, 'NAME EXISTS UNDER DIFFERENT ID');
END F_CHECK;
A function MUST ALWAYS RETURN a VALUE of proper data type. Else, it would throw the following error:
ORA-06503: PL/SQL: Function returned without value
Cause: A call to PL/SQL function completed, but no RETURN statement was executed.
Action: Rewrite PL/SQL function, making sure that it always returns a value of a proper type.
Read ORA-06503: PL/SQL: Function returned without value
DB version : 11.2.0.2.0
Let’s see the various scenarios of this error :
Without a RETURN statement in the function body and without exception handler(most stupid way):
SQL> set serveroutput on
SQL> CREATE OR REPLACE FUNCTION f_test(i_val NUMBER)
2 RETURN NUMBER AS
3 o_val NUMBER;
4 BEGIN
5 SELECT 100 / i_val
6 INTO o_val
7 FROM DUAL;
8 END;
9 /
Function created
SQL> select f_test(100) from dual;
select f_test(100) from dual
ORA-06503: PL/SQL: Function returned without value
ORA-06512: at "F_TEST", line 8
Now, in the above code, the mathematical logic was correct, hence there was no SQL error to override the PL/SQL error. Let’s see how ORA-01476 will override the ORA-06503 error.
SQL> CREATE OR REPLACE FUNCTION f_test(i_val NUMBER)
2 RETURN NUMBER AS
3 o_val NUMBER;
4 BEGIN
5 SELECT 100 / i_val
6 INTO o_val
7 FROM DUAL;
8 END;
9 /
Function created
SQL> select f_test(0) from dual;
select f_test(0) from dual
ORA-01476: divisor is equal to zero
ORA-06512: at "F_TEST", line 5
Well, that’s quite obvious, isn’t it?
Without a RETURN statement in the exception handler(most common mistake):
SQL> CREATE OR REPLACE FUNCTION f_test(i_val NUMBER)
2 RETURN NUMBER AS
3 o_val NUMBER;
4 BEGIN
5 SELECT 100 / i_val
6 INTO o_val
7 FROM DUAL;
8
9 RETURN o_val;
10
11 EXCEPTION
12 WHEN OTHERS THEN
13 NULL;
14 END;
15 /
Function created
SQL> select f_test(0) from dual;
select f_test(0) from dual
ORA-06503: PL/SQL: Function returned without value
ORA-06512: at "F_TEST", line 14
Now let’s put a RETURN statement at required places and the code should work fine without any error:
SQL> CREATE OR REPLACE FUNCTION f_test(i_val NUMBER)
2 RETURN NUMBER AS
3 o_val NUMBER;
4 BEGIN
5 SELECT 100 / i_val
6 INTO o_val
7 FROM DUAL;
8
9 RETURN o_val;
10
11 EXCEPTION
12 WHEN OTHERS THEN
13 DBMS_OUTPUT.PUT_LINE('Came inside Exception handler');
14 RETURN 0;
15 END;
16 /
Function created
SQL> select f_test(0) from dual;
F_TEST(0)
0
Came inside Exception handler
Bottom line is that :
A function MUST ALWAYS RETURN a value of proper datatype, no matter from the body or exception.
We must do something with the error not just return junk. We must RAISE/log error and handle it, do something about the error so that underlying process has no impact.
Lastly, not to forget, EXCEPTION WHEN OTHERS THEN NULL; –> is itself a bug in the code waiting for its chance to break the code.
I use TOAD for executing my procedures and running my PL/SQL scripts.
Recently, I tried the dbms_obfuscation_toolkit.md5() function in PL/SQL to retrieve encrypted text of the string I pass as a parameter. I found that, the function gives the proper result when I call the function as a separate statement. However, when I call it via a SELECT query, it does not respond the same format.
Below, I have mentioned this peculiar case with the results that appear once I run it in TOAD application.
--CASE:1 (Proper result is obtained here)
DECLARE
vinput varchar2(255) := 'SRINI';
well varchar2(50);
BEGIN
WELL := DBMS_OBFUSCATION_TOOLKIT.MD5(input_string =>VINPUT);
dbms_output.put_line(well);
END;
Output: Áýg¿Zq!Ù´¿Ke>ÏQ
--CASE:2 (Unexpected response is obtained here)
DECLARE
vinput varchar2(255) := 'SRINI';
well varchar2(50);
BEGIN
SELECT DBMS_OBFUSCATION_TOOLKIT.MD5(VINPUT)
INTO WELL
FROM DUAL;
dbms_output.put_line(well);
END;
Error Output:
ORA-06550: line 5, column 37:
PLS-00307: too many declarations of 'MD5' match this call
ORA-06550: line 5, column 12:
PL/SQL: ORA-00904: "DBMS_OBFUSCATION_TOOLKIT"."MD5": invalid identifier
ORA-06550: line 5, column 5:
PL/SQL: SQL Statement ignored
Why is there such a disparity in the results, even though both of them are syntactically and semantically correct ? Please provide your opinion and feedback.
Your function calls are not identical, syntactically nor semantically. If you call the function in PL/SQL as you did in SQL, you will get the same exact error:
SQL> DECLARE
2 vinput VARCHAR2(255) := 'SRINI';
3 well VARCHAR2(50);
4 BEGIN
5 WELL := DBMS_OBFUSCATION_TOOLKIT.MD5(vinput);
6 dbms_output.put_line(well);
7 END;
8 /
ORA-06550: Ligne 5, colonne 12 :
PLS-00307: too many declarations of 'MD5' match this call
This is because the function called MD5 is overloaded in the package DBMS_OBFUSCATION_TOOLKIT and you need to specify which function exactly to call.
In Oracle 11g, you can use the same synthax as PL/SQL:
SQL> DECLARE
2 vinput VARCHAR2(255) := 'SRINI';
3 well VARCHAR2(50);
4 BEGIN
5 SELECT DBMS_OBFUSCATION_TOOLKIT.MD5(input_string => VINPUT)
6 INTO WELL FROM DUAL;
7 dbms_output.put_line(well);
8 END;
9 /
Áýg¿Zq!Ù´¿Ke>ÏQ
PL/SQL procedure successfully completed
Before 11g, you can define a wrapper function:
SQL> CREATE OR REPLACE FUNCTION wrap_md5(input_string VARCHAR2)
2 RETURN dbms_obfuscation_toolkit.varchar2_checksum
3 IS
4 BEGIN
5 RETURN dbms_obfuscation_toolkit.md5(input_string => input_string);
6 END;
7 /
Function created
SQL> DECLARE
2 vinput VARCHAR2(255) := 'SRINI';
3 well VARCHAR2(50);
4 BEGIN
5 SELECT wrap_md5(VINPUT)
6 INTO WELL FROM DUAL;
7 dbms_output.put_line(well);
8 END;
9 /
Áýg¿Zq!Ù´¿Ke>ÏQ
PL/SQL procedure successfully completed
select dbms_obfuscation_toolkit.md5(input_string => 'SRINI') from dual; use this in select for the such type of function uses input string.
After doing about two hours' worth of research on various issues with execute immediate and error handling, I don't think I've encountered exactly the problem I am about to present. I have reduced my issue to the sample below. The issue is:
When I execute a procedure create in immediate mode with the create text in a variable and a syntax error in the create text, the execute immediate call throws an error indicating that it failed. But I can't get at the underlying error or its text.
However, when I create the same proc directly in sqlplus (w/o intermediate mode), getting the error works perfectly.
So, how does one get at the error that causes immediate mode to fail?
I have interspersed the output with my comments (MOP) and reduced the number of blank lines:
SQL*Plus: Release 10.2.0.1.0 - Production on Mon Mar 25 15:57:06 2013
Copyright (c) 1982, 2005, Oracle. All rights reserved.
Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
SQL> set serveroutput on
SQL> declare
2 eSQL varchar(568);
3 begin
4 eSQL := 'create or replace procedure MOPMOP
5 as
6 begin
7 select 1 from dual;
8 end;
9 ';
10 execute immediate eSQL;
11 end;
12 /
ERROR:
ORA-24344: success with compilation error
ORA-06512: at line 10
Warning: PL/SQL compilation errors.
This is good. There's a compilation error. So, now let me ask what that compilation error is:
SQL> show errors;
No errors.
This is no good. There's a compilation error. What was it!?!?!? Contrast that with:
SQL> create or replace procedure MOPMOP
2 as
3 begin
4 select 1 from dual;
5 end;
6 /
Warning: Procedure created with compilation errors.
This is good. There's a compilation error. So, now let me ask what that compilation error is:
SQL> show errors;
Errors for PROCEDURE MOPMOP:
LINE/COL ERROR
-------- -----------------------------------------------------------------
4/5 PLS-00428: an INTO clause is expected in this SELECT statement
SQL>
And this is good. There's a compilation error. And now I know what is is.
So, how do I get intermediate mode to spit out the underlying (00428) error?
when you run show errors on its own it will show the errors for any prior "create" / "alter" that sqlplus itself did, which is this case is nothing (as it was hidden from sqlplus because of the dynamic SQL)
you have to expcitly say you want to see the errors of the procedure for this case:
SQL> declare
2 eSQL varchar(568);
3 begin
4 eSQL := 'create or replace procedure MOPMOP
5 as
6 begin
7 select 1 from dual;
8 end;
9 ';
10 execute immediate eSQL;
11 end;
12 /
ERROR:
ORA-24344: success with compilation error
ORA-06512: at line 10
Warning: PL/SQL compilation errors.
SQL> show errors procedure mopmop
Errors for PROCEDURE MOPMOP:
LINE/COL ERROR
-------- -----------------------------------------------------------------
4/5 PLS-00428: an INTO clause is expected in this SELECT statement
SQL>
as the create ran server side and not through sqlplus, sqlplus didn't know about the failure, so couldnt fetch the errors correctly.
also if you had a prior failure, it would retain that:
SQL> create or replace procedure MOPMOP2
2 as
3 begin
4 select 1 from dual;
5 end;
6 /
Warning: Procedure created with compilation errors.
SQL> declare
2 eSQL varchar(568);
3 begin
4 eSQL := 'create or replace procedure MOPMOP
5 as
6 begin
7 select 1 from dual;
8 end;
9 ';
10 execute immediate eSQL;
11 end;
12 /
ERROR:
ORA-24344: success with compilation error
ORA-06512: at line 10
Warning: PL/SQL compilation errors.
SQL> show errors
Errors for PROCEDURE MOPMOP2:
LINE/COL ERROR
-------- -----------------------------------------------------------------
4/5 PLS-00428: an INTO clause is expected in this SELECT statement
SQL>
You cannot use select 1 from dual... in PL/SQL. Must use select into. In both cases you are creating PL/SQL procedures not some SQL script.
declare
x number;
begin
select 1 into x from dual;
end;
/
declare
procedure MOPMOP
as
x number;
begin
select 1 into x from dual;
dbms_output.put_line(x);
end MOPMOP;
begin
MOPMOP;
end;
/
Avoid ORA-24344 - although I probably would never use dynamic sql for this:
declare
eSQL varchar(568);
x number;
begin
eSQL := 'create or replace procedure MOPMOP as
begin
select 1 into x from dual;
end;';
execute immediate eSQL;
EXCEPTION WHEN OTHERS THEN NULL;
end;
/
I am new to Oracle and SQL and am trying to execute a simple test function from within SQL*Plus. My function is called tf (for Test Function) and it is defined in a file called tf.sql as follows ;
create or replace
function
tf
(
arg1 in varchar2
)
return number
as
return_value number;
begin
return_value := 0;
dbms_output.put_line('Argument 1 = ' || arg1);
return return_value;
end;
/
I am able to successfully load this function into Oracle using the following command ;
SQL> start ./tf.sql
As a result of executing this command, SQL*Plus simply states ;
Function created.
When I then execute the following command from the SQL*Plus command prompt (after I have invoked set serveroutput on) ;
SQL> exec dbms_output.put_line(SYSTEM.TF('Hello'));
I get the following output ;
Argument = Hello
0
PL/SQL procedure successfully completed.
Now, if I attempt to execute my function directly from the SQL*Plus command prompt, using the following command ;
SQL> exec SYSTEM.TF('Hello');
then I get presented with the following error message from SQL*Plus ;
BEGIN SYSTEM.TF('Hello'); END;
*
ERROR at line 1:
ORA-06550: line 1, column 7:
PLS-00221: 'TF' is not a procedure or is undefined
ORA-06550: ;ine 1, column 7
PL/SQL: Statement ignored
Is anyone able to shed any light on this for me? I can't work out why my function appears to execute successfully in the first case, but not in the second case.
If I execute the following command from the SQL*Plus command prompt ;
SQL> select * from user_objects where object_name = 'TF';
then I get the following results returned ;
OBJECT_NAME
-----------
TF
SUBOBJECT_NAME
--------------
OBJECT_ID
---------
74475
DATA_OBJECT_ID
--------------
OBJECT_TYPE
-----------
FUNCTION
CREATED
-------
05-FEB-12
LAST_DDL_
---------
05-FEB-12
TIMESTAMP
---------
2012-02-05:02:11:15
STATUS
------
VALID
T
-
N
G
-
N
S
-
N
EDITION_NAME
------------
1
Any help on this would be immensely appreciated.
Thanks in advance.
Craig
exec does not work with functions because it does not know what to do with the return value. This is similar to a regular PL/SQL statement; if you call a function you must assign the return value to something.
If you want to use a function in SQL*Plus, you should use SQL instead:
select tf('asdf') from dual;
Also, you should never create objects in SYSTEM. This can cause some really weird problems.
Following on from #jonearles answer,which highlights the difference between a function and a procedure from SQL*Plus' perspective, and #MS Stp's comment, one way to run it is:
variable rc number;
exec :rc := tf('Hello');
Argument = Hello
PL/SQL procedure successfully completed.
To see the return code you can then do:
print rc
0
exec is really just shorthand for an anonymous PL/SQL block, as you can see from the error message you got. variable lets you declare a bind variable at SQL*Plus level, rather than in the block. You can also declare the argument as bind variable, and set it with a separate exec call:
variable rc number;
variable arg varchar2(5);
exec :arg := 'Hello';
exec :rc := tf(:arg);
I often use this construct for testing an existing procedure call, e.g. something copied from Pro*C code, without having to replace the variables in that call with fixed values. It can make it easier to call repeatedly with different arguments, and you can reuse variables in several calls - so you could pass :rc to another function later.