Oracle PL/SQL: How to find unused variables in a long package? - variables

Please suppose you have an Oracle PL/SQL package of about 200,000 rows of code.
Is there any fast way to detect variables declared, but not used in the package?
Thank you in advance for your kind help.
EDIT (April 7th, 2014): I am using Oracle 10G.
EDIT: I am looking for a pure PL/SQL solution.

The following only applies to 11g R2. It looks like PL/Scope has become available in 11g R1.
You won't get information about unused variables with PLSQL_WARNINGS='ENABLE:ALL':
SQL> !cat test.sql
set serveroutput on
alter session set plsql_warnings = 'ENABLE:ALL';
create or replace procedure foo is
v_a number;
v_b varchar2(10);
begin
dbms_output.put_line('hello world!');
end;
/
show errors
exec foo
SQL> #test
Session altered.
SP2-0804: Procedure created with compilation warnings
Errors for PROCEDURE FOO:
LINE/COL ERROR
-------- -----------------------------------------------------------------
1/1 PLW-05018: unit FOO omitted optional AUTHID clause; default
value DEFINER used
hello world!
PL/SQL procedure successfully completed.
SQL>
As you can see the only reported warning is not related to the unused variables at all. Instead PL/Scope has to be used.
The following example has bee derived from Oracle 11g – Generating PL/SQL Compiler Warnings (Java style) using PL/Scope:
SQL> alter session set plscope_settings = 'identifiers:all';
Session altered.
SQL> alter procedure foo compile;
SP2-0805: Procedure altered with compilation warnings
SQL> show errors
Errors for PROCEDURE FOO:
LINE/COL ERROR
-------- -----------------------------------------------------------------
1/1 PLW-05018: unit FOO omitted optional AUTHID clause; default
value DEFINER used
SQL> #plsql-unused-variables.sql
Enter value for name: foo
old 10: where object_name = upper('&name')
new 10: where object_name = upper('foo')
Enter value for type: procedure
old 11: and object_type = upper('&type')
new 11: and object_type = upper('procedure')
COMPILER_WARNING
--------------------------------------------------------------------------------
V_B: variable is declared but never used (line 3)
V_A: variable is declared but never used (line 2)
SQL>
The script plsql-unused-variables.sql is just a cut and paste from the blog post mentioned above. Because I found it useful I have also made the script available in Bitbucket.

Set your session to report all warnings:
ALTER SESSION SET PLSQL_WARNINGS='ENABLE:ALL';
then compile your code. If the compilation indicates that there are errors, as in you get something like the following:
SP2-0804: Procedure created with compilation warnings
display any errors:
SHO ERR
If you have any unreferenced variables they should be mentioned in the list of errors. Alternatively, use a tool like PL/SQL Developer which automatically shows you these errors following a compile.
Share and enjoy.

PL/SQL waits to allocate memory until you assign the variable, then only
allocates as much storage as needed. So no need to worry about consuming memory. (Source)
So finding and fixing unused variables is just a housekeeping exercise and won't improve the performance of the package. In other words, the simplest solution would be to do nothing.
If you're worried about unused variables anyway, you might be able to find them just by parsing the package source with command-line tools such as grep, sort and uniq (especially if they follow a coding standard such as starting all variables with v_).

Related

Why Plsql package variables and constants cannot be used in Sql?

Create some package:
create or replace package my_package is
some_var number := 10;
end;
and use it in Sql:
select my_package.some_var from dual
So it gives error PLS-221.
P.S. I know that I can use wrapper function. My question is why this is not allowed. Can anyone point to the documentation where the reason?
I am going to jump and try to answer your question. It is one I heard of sometimes and indeed has been for a while as a Enhancement Request ( Base ER: ENH 6525013 ), as it was very well pointed by #Anum Kumar.
Why is not possible ? Well, I think the Oracle developers of PL/SQL though about Packages merely as collections of logical subprograms and routines. So make available variables directly to external APIs ( OCI or Java ) was never the intention of the concept.
You don't really have a document link telling you that is not possible, as far as I know, but if you read the documentation of the concept itself, you might get some insights:
A package is a schema object that groups logically related PL/SQL types, variables, constants, subprograms, cursors, and exceptions. A package is compiled and stored in the database, where many applications can share its contents. A package always has a specification, which declares the public items that can be referenced from outside the package. In either the package specification or package body, you can map a package subprogram to an external Java or C subprogram by using a call specification, which maps the external subprogram name, parameter types, and return type to their SQL counterparts.
That is the key, you can map any subprogram of a package to any external Java, C routine; but you can't reference a constant variable directly without the corresponding subprogram ( in your case a function ).
However, you can't use it on SQL, but you can in PL/SQL. Keep in mind that Oracle contains different areas in the Library cache to handle SQL and PLSQL. The library cache holds executable forms of PL/SQL programs and Java classes. These items are collectively referred to as program units.
Example
CREATE OR REPLACE PACKAGE pkg IS
n NUMBER := 5;
END pkg;
/
Package created.
SQL> CREATE OR REPLACE PACKAGE sr_pkg IS
PRAGMA SERIALLY_REUSABLE;
n NUMBER := 5;
END sr_pkg;
/
Package created.
SQL> select sr_pkg.n from dual ;
select sr_pkg.n from dual
*
ERROR at line 1:
ORA-06553: PLS-221: 'N' is not a procedure or is undefined
SQL> set serveroutput on
SQL> BEGIN
pkg.n := 10;
sr_pkg.n := 10;
END;
/
PL/SQL procedure successfully completed.
SQL> BEGIN
DBMS_OUTPUT.PUT_LINE('pkg.n: ' || pkg.n);
DBMS_OUTPUT.PUT_LINE('sr_pkg.n: ' || sr_pkg.n);
END;
/
pkg.n: 10
sr_pkg.n: 5
PL/SQL procedure successfully completed.
In the example above, I can't use the SQL engine to reference a constant variable, as I would need a subprogram or routine ( this is an OCI call, but it might very well be a Java program, ProC, etc. I specifically used one of the packages as serially_reusable that you can check how the variable does not change even if I try to.
However, if I don't use the SQL engine, I can use it without any problem within PL/SQL.
I hope it clarifies.

Show errors in sql plus

I have a file myFile.sql that contains a list of script to compile :
#"Directory\package1.sql"
#"Directory\package2.sql"
#"Directory\package3.sql"
#"Directory\package4.sql"
I have the following script:
SPOOL Directory\Upgrade.log
#"Directory\myFile.sql"
SPOOL OFF
Some packages in myFile.sql have errors, but in Upgrade.log I do not have the details of the errors, I have the message Warning : Package body created with compilation errors.
How can I add the error detail without adding SHOW ERR after each line in MyFile.sql ?
In upgrade.log I want have this:
Package1 created
Warning Package body created with compilation errors.
**Error detail1**
Package2 created
Warning Package body created with compilation errors.
**Error detail2**
I need a hook in sqlplus to show error automatically after each package creation if there is an error
Thanks for your help.
One method is to query dictionnary view USER_ERRORS, or ALL_ERRORS.
From the documentation:
ALL_ERRORS describes the current errors on the stored objects accessible to the current user.
USER_ERRORS gives the same information for objects owned by the current user.
From Oracle 11.1 onward, you could use the SQLPlus error logging feature. You can read more about SQL*Plus error logging – New feature release 11.1.
SQL*Plus error logging is set OFF by default. So, you need to set errorlogging on to use the SPERRORLOG table.
Demo:
Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
SQL> desc sperrorlog;
Name Null? Type
----------------------------------------- -------- ----------------------------
USERNAME VARCHAR2(256)
TIMESTAMP TIMESTAMP(6)
SCRIPT VARCHAR2(1024)
IDENTIFIER VARCHAR2(256)
MESSAGE CLOB
STATEMENT CLOB
SQL> truncate table sperrorlog;
Table truncated.
SQL> set errorlogging on;
SQL> selct * from dual;
SP2-0734: unknown command beginning "selct * fr..." - rest of line ignored.
SQL> select timestamp, username, script, statement, message from sperrorlog;
TIMESTAMP USERNAME STATEMENT MESSAGE
----------- -------- ------------------ -------
17-APR-2020 SCOTT selct * from dual; SP2-0734: unknown command beginning "selct * fr..." - rest of line ignored.
Similarly, you can capture PLS errors too. They will start with error code SP.

DB2 stored procedure gave syntax errors

I am creating a stored procedure for db2. But it is giving an error saying that
"SQL Error [42601]: An unexpected token "END-OF-STATEMENT" was found
following "SS_TOKEN_BAK". Expected tokens may include: " END IF"..
SQLCODE=-104, SQLSTATE=42601, DRIVER=4.23.42".
Following is my stored procedure.
CREATE OR REPLACE PROCEDURE TOKEN_CLEANUP_SP
BEGIN
DECLARE batchSize INTEGER;
-- ------------------------------------------
-- CONFIGURABLE ATTRIBUTES
-- ------------------------------------------
SET batchSize = 10000; -- SET BATCH SIZE FOR AVOID TABLE LOCKS [DEFAULT : 10000]
-- ------------------------------------------------------
-- BACKUP IDN_OAUTH2_ACCESS_TOKEN TABLE
-- ------------------------------------------------------
IF EXISTS (SELECT TABLE_NAME FROM TABLES WHERE TABLE_NAME = 'IDN_OAUTH2_ACCESS_TOKEN_BAK')
THEN
DROP TABLE IDN_OAUTH2_ACCESS_TOKEN_BAK;
END IF;
END/
Is anyone face this type of issue. Any help on this would be much appreciated.
Verify that you have the end-of-statement delimiter configured correctly for whatever tool submits the 'CREATE OR REPLACE' procedure. Different tools have different ways to configure the block terminator (alternatively known end of statement delimiter). For command line scripts, use --#SET TERMINATOR / at the start of the file, other ways are possible.
Your code sample shows / as the block terminator, so you might want to use that character as the block delimiter. The semi-colon terminates statements inside the block.
Separately you should see that your code won't compile if the specified table does not exist in the implied schema at compilation time, because you are using static SQL. You may want to use dynamic SQL instead for the drop table statement (search for 'EXECUTE IMMEDIATE' examples).

oracle sql developer first time user

I am new to plsql and trying to use oracle sql developer, I try to run a simple procedure with dbms output line and i get the following error,
ora-00904
, the code is
create or replace PROCEDURE proc_101 IS
v_string_tx VARCHAR2(256) := 'Hello World';
BEGIN
dbms_output.put_line(v_string_tx);
END;
whether i click the run(green colour) or debug(red colour) i get the same error.
You can see from the above code, procedure doesn't access any objects but still i get the same error.
Your procedure is fine. You may not have permissions to be able to Create a Procedure. If this is the case test your procedure/code without actually Creating it in the Database first. For example, when I'm testing code in my Production database my oracle user cannot Create Procedures, Packages, Tables etc... And so I test my Procedures within my Own PL/SQL Blocks. When the code is good to go I can get a database administrator to Create the Procedures and/or Packages for me.
The below screenshot is code that simply tests the Procedure:
The below screenshot is code that does much more and tests the Procedure from within a PL/SQL Block
For more advanced situations this allows you to do so much more as you can create all sorts of Procedures/Functions and/or Cursors and test them immediately without needing to CREATE these objects in your Oracle Database.
I'd say that there's some other code in the worksheet which raises that error, not just the CREATE PROCEDURE you posted. For example, something like this SQL*Plus example (just to show what's going on - you'd get the same result in SQL Developer):
SQL> select pixie from dual;
select pixie from dual
*
ERROR at line 1:
ORA-00904: "PIXIE": invalid identifier
SQL>
SQL> create or replace PROCEDURE proc_101 IS
2 v_string_tx VARCHAR2(256) := 'Hello World';
3 BEGIN
4 dbms_output.put_line(v_string_tx);
5 END;
6 /
Procedure created.
SQL>
See? The first part raised ORA-00904 as there's no PIXIE column in DUAL, while the procedure is created correctly.
So - remove code which fails and everything should be OK.
Check with your DBA to make sure the dbms_output package has been installed on your database, and that you have permissions on it.

Executing a Stored Procedure in Oracle

I have a stored procedure, on Toad for Oracle I am calling the procedure
using
SELECT FROM PKGName.ProcedureName(1,'10/10/2010','10/23/2010',7,7)
FROM DUAL
I have 3 output parameter on this procedure as well I am getting an
ORA-00904: PKGName.ProcedureName : Invalid Identifier
Do have to mention the output parameter on the procedure call as well? If yes how can I use it?
You cannot use a procedure in a SELECT statement. Functions yes (with appropriate return types), procedures no. Items in a SELECT list must be expressions, which must resolve to a value. A procedure does not meet this criteria.
And yes, you do need to mention the output variables in your parameter list. The procedure is going to set those parameters to some values, there needs to be a output parameter specified for each to receive them. #schurik shows you how it is usually done in PL/SQL. #Datajam is close to how you'd do it in SQL*Plus, but leaves out the output parameters:
SQL> var num_var number
SQL> var txt_var varchar2(15)
SQL> var txt_var2 varchar2(20)
SQL> exec PKGName.ProcedureName(1,'10/10/2010','10/23/2010',7,7, :num_var, :txt_var, :txt_var2);
PL/SQL procedure successfully completed
num_var
---------------
42
txt_var
-----------------
some text
txt_var2
-------------------
some other text
SQL>
declare
-- declare variables to keep output values
output_par_1 varchar2(100);
output_par_2 number(10);
...
begin
PKGName.ProcedureName(1,'10/10/2010','10/23/2010',output_par_1,output_par_2);
-- display output values
dbms_output.put_line('output_par_1: ' || output_par_1);
dbms_output.put_line('output_par_2: ' || output_par_2);
end;
/
If you want to be able to call procedures from select, wrap it with a function or table function. See here for more details: http://technology.amis.nl/blog/1017/calling-stored-procedures-using-plain-sql-for-when-sql-is-allowed-but-calls-to-stored-procedures-are-not (heck the title is almost an article hehehe).
Yes, you must provide all arguments. Declare a variable of the appropriate type and pass it as an output argument.
You shouldn't really call a procedure using a SELECT statement (and even if you did, the call would be before the FROM part).
Instead, use a SQL*Plus prompt (I think Toad has a built-in SQL*Plus interface):
exec PKGName.ProcedureName(1,'10/10/2010','10/23/2010',7,7);
The code in the question is syntactically wrong, it should be
SELECT PKGName.ProcedureName(1,'10/10/2010','10/23/2010',7,7) FROM DUAL
However, this would only work for functions. But as it's obviously working in Toad I assume the poster actually did have a function. I also made the assumption that using SQL was a prerequisite.
If the point of the question was how to make use of multiple output parameters - try creating a user defined type. Maybe the question should then be renamed to "Calling a procedure with output parameters from SQL in Oracle".
Otherwise simple wrapper function without output parameters would do the job.