Variable not being replaced (learning Dynamic PL/SQL) - oracle

The below code returns error ORA-00942: table or view does not exist, I think may be because PL/SQL runtime engine(or something I don't know what) is trying to treat table_in as a Table but why would it do so, I have already table_in declared as variable.
The ex26011601 table exists with values in the same schema.
set serveroutput on
declare
function tabcount (table_in in varchar2)
return pls_integer
is
l_return pls_integer;
begin
select count(*) into l_return from table_in;
return l_return;
end;
begin
dbms_output.put_line(tabcount('ex26011601'));
end;
I understand EXECUTE IMMEDIATE would solve the purpose. What I am trying to get is why is it necessary and whats wrong with current statement that 'table_in' could not be treated as variable even after being declared in the scope. Or what is the reason why a variable is not expected there?

I understand EXECUTE IMMEDIATE would solve the purpose. What I am
trying to get is why is it necessary and whats wrong with current
statement that 'table_in' could not be treated as variable even after
being declared in the scope.
As per oracle documentation : Static SQL
A PL/SQL static SQL statement can have a PL/SQL identifier wherever its SQL counterpart can have a placeholder for a bind variable. The PL/SQL identifier must identify either a variable or a formal parameter.To use PL/SQL identifiers for table names, column names, and so on, use the EXECUTE IMMEDIATE statement
In PL/SQL, you need dynamic SQL to run when :
SQL whose text is unknown at compile time
For example, a SELECT statement that includes an identifier that is unknown at compile time (such as a table name) or a WHERE clause in
which the number of sub clauses is unknown at compile time.
SQL that is not supported as static SQL
Dynamic SQL

Yes, as you said oracle pl/sql syntax does not allowing that, pass table name by variable. As you also said you can do it only by dynamic sql and execute immediate:
execute immediate 'select count(*) from ' || table_in
into l_return;

Related

SQL injection for Stored Procedure

I'm evaluating the possibility of doing SQL injection for my sp.
I have tried using this to do SQL injection but didn't manage to inject (meaning the injection text was inserted to table as per normal):
data'; DROP TABLE my_table; --
How should I try SQL injection? Or the SP is so safe that SQL Injection is prevented somehow?
My reduced SP as below:
#ID int,
#AIType varchar(1),
#parent varchar(20),
#child varchar(20),
AS
BEGIN
SET NOCOUNT ON;
-- Insert statements for procedure here
BEGIN TRY
UPDATE AI_Grouping
SET AIType=#AIType,
parent=#parent,
child=#child,
WHERE ID=#ID
END TRY
BEGIN CATCH
-- Catch exceptions
END CATCH
END
EDIT:
In case it helps - at front end, I have a field length validation which is consistent with SP variable type. Some fields are limited max 8 chars, some are max 20 chars (like above example). Maybe the injection example that I tried above is a bad example, because the length is more than 20 chars...
The ultimate question is, is my SP vulnerable to SQL injection or not?
From the article: How to write SQL injection proof PL/SQL
Distinguishing between compile-time-fixed SQL statement text and
run-time-created SQL statement text
We define the term compile-time-fixed SQL statement text to mean the text of a
SQL statement that cannot change at run time and that can be confidently
determined by reading the source code. More precisely, it is the text of a
SQL statement that is a PL/SQL static varchar2 expression14. The value of a
PL/SQL static varchar2 expression cannot change at run time and could be precomputed at compile time.
The SQL statement text for embedded SQL is composed by the PL/SQL
compiler and cannot change at run time. Therefore, embedded SQL definitely
executes only compile-time-fixed SQL statement text15.
However, it can easily be arranged that any of PL/SQL’s methods for executing
dynamic SQL will, at a particular call site, execute only compile-time-fixed SQL.
So your code as it is, is safe.
To distinguish between compiled-time-fixed SQL and run-time-created SQL here are two samples:
compiled-time-fixed SQL
CREATE PROCEDURE remove_emp (p_employee_id NUMBER) AS
BEGIN
-- here the delete command is immutable, therefore sql injection safe
DELETE FROM employees
WHERE employees.employee_id = p_employee_id;
END;
run-time-created SQL
CREATE PROCEDURE remove_emp (p_employee_id VARCHAR2) AS
BEGIN
-- here the delete command is dynamically created allowing
-- sql injection
execute immediate 'DELETE FROM employees
WHERE employees.employee_id = ' || p_employee_id || ';';
END;

Create and execute an Oracle Stored Procedure for a select query in SQL Developer

I am using Oracle SQL Developer with Oracle 11g.
I face a strange issue creating a simple stored procedure for a Select query that doesn't need any input parameters as such. It just selects from a user defined function from the "dual" table.
These are the issues I face:
I am not able to create a procedure with no input parameters (because I don't need to use any parameter value in the select!). But the syntax does not allow me to have zero parameters, it demands a REF_CURSOR out parameter. Is the REF_CURSOR a compulsory thing in SQL Developer procedures? Is it anything to do with procedures involving a Select query?
The select query demands an INTO clause (a variable to copy the query result) in SQL developer. Is it mandatory?
Even if I used an INTO clause, I can't figure out the syntax to declare a temporary variable to copy the query result into this variable. So that I can use this out variable in my program snippet.
This is my procedure block:
Create or Replace PROCEDURE Getmarketdetails
AS
DECLARE temp varchar;
BEGIN
SELECT *
INTO temp from dual;
END Getmarketdetails;
I get these errors on compiling the procedure:
PLS-00103: Encountered the symbol "DECLARE" when expecting one of
the following: begin function pragma procedure subtype type
current cursor delete exists prior external language The
symbol "begin" was substituted for "DECLARE" to continue.
PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following: ( begin case declare end exception exit for goto if loop mod null pragma raise return select update while with << continue close current delete fetch lock insert open rollback savepoint set sql execute commit forall merge pipe purge .
All I need is the perfect script syntax to create the stored procedure for this and also execute it using the exec command. And some clarifications to questions raised above. Appreciate if someone can oblige ! :)
Your syntax is incorrect - you need to declare a length for your varchar and you don't need the declare.
Create or Replace PROCEDURE Getmarketdetails
AS
temp varchar(100);
BEGIN
SELECT *
INTO temp from dual;
END Getmarketdetails;
Create or Replace PROCEDURE Getmarketdetails
AS
temp varchar2(20);
BEGIN
SELECT 'stack overflow' INTO temp from dual;
Dbms_output.put_line(temp);
END Getmarketdetails;
Some modification done in your procedure. Don't write declare and mention variables as per your need.

Why cannot I use bind variables in DDL/SCL statements in dynamic SQL?

I am trying to execute an SQL command within dynamic SQL with bind variables:
-- this procedure is a part of PL/SQL package Test_Pkg
PROCEDURE Set_Nls_Calendar(calendar_ IN VARCHAR2)
IS
BEGIN
EXECUTE IMMEDIATE
'ALTER SESSION
SET NLS_CALENDAR = :cal'
USING IN calendar_;
END Set_Nls_Calendar;
Then on the client side, I am trying to invoke the procedure:
Test_Pkg.Set_Nls_Calendar('Thai Buddha');
But this get's me ORA-02248: invalid option for ALTER SESSION.
And my question is: Why cannot I use bind variables in DDL/SCL statements in dynamic SQL?
Bind variables are not allowed in DDL statements. So following statements will cause errors:
Example #1: DDL statement. Will cause ORA-01027: bind variables not allowed for data definition operations
EXECUTE IMMEDIATE
'CREATE TABLE dummy_table ( dummy_column NUMBER DEFAULT :def_val )'
USING 42;
Example #2: DDL statement. Will cause ORA-00904: : invalid identifier
EXECUTE IMMEDIATE
'CREATE TABLE dummy_table ( :col_name NUMBER )'
USING var_col_name;
Example #3: SCL statement. Will cause ORA-02248: invalid option for ALTER SESSION
EXECUTE IMMEDIATE
'ALTER SESSION SET NLS_CALENDAR = :cal'
USING var_calendar_option;
Problem
To understand why this happens, we need to look at How Dynamic SQL Statements Are Processed.
Typically, an application program prompts the user for the text of a SQL statement and the values of host variables used in the statement. Then Oracle parses the SQL statement. That is, Oracle examines the SQL statement to make sure it follows syntax rules and refers to valid database objects. Parsing also involves checking database access rights1, reserving needed resources, and finding the optimal access path.
1 Emphasis added by answerer
Note that parsing step happens before binding any variables to the dynamic statement. If you examine the above four examples, you will realize that there is no way for the parser to guarantee the syntactical validity of these dynamic SQL statements without knowing the values for bind variables.
Example #1: Parser cannot tell if the bind value will be valid. What if instead of USING 42, programmer wrote USING 'forty-two'?
Example #2: Parser cannot tell if :col_name would be a valid column name. What if the bound column name was 'identifier_that_well_exceeds_thirty_character_identifier_limit'?
Example #3: Values for NLS_CALENDAR are built in constants (for a given Oracle version?). Parser cannot tell if the bound variable will have a valid value.
So the answer is that you cannot bind schema elements such as table names, column names in dynamic SQL. Nor you can bind built in constants.
Solution
The only way to achieve referencing schema elements/constants dynamically is to use string concatenation in dynamic SQL statements.
Example #1:
EXECUTE IMMEDIATE
'CREATE TABLE dummy_table ( dummy_column NUMBER DEFAULT ' || to_char(42) || ')';
Example #2:
EXECUTE IMMEDIATE
'CREATE TABLE dummy_table (' || var_col_name || ' NUMBER )';
Example #3:
EXECUTE IMMEDIATE
'ALTER SESSION SET NLS_CALENDAR = ''' || var_calendar_option || '''';

DDL statements in PL/SQL?

I am trying the code below to create a table in PL/SQL:
DECLARE
V_NAME VARCHAR2(20);
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE TEMP(NAME VARCHAR(20))';
EXECUTE IMMEDIATE 'INSERT INTO TEMP VALUES(''XYZ'')';
SELECT NAME INTO V_NAME FROM TEMP;
END;
/
The SELECT statement fails with this error:
PL/SQL: ORA-00942: table or view does not exist
Is it possible to CREATE, INSERT and SELECT all in a single PL/SQL Block one after other?
I assume you're doing something like the following:
declare
v_temp varchar2(20);
begin
execute immediate 'create table temp(name varchar(20))';
execute immediate 'insert into temp values(''XYZ'')';
select name into v_name from temp;
end;
At compile time the table, TEMP, does not exist. It hasn't been created yet. As it doesn't exist you can't select from it; you therefore also have to do the SELECT dynamically. There isn't actually any need to do a SELECT in this particular situation though you can use the returning into syntax.
declare
v_temp varchar2(20)
begin
execute immediate 'create table temp(name varchar2(20))';
execute immediate 'insert into temp
values(''XYZ'')
returning name into :1'
returning into v_temp;
end;
However, needing to dynamically create tables is normally an indication of a badly designed schema. It shouldn't really be necessary.
I can recommend René Nyffenegger's post "Why is dynamic SQL bad?" for reasons why you should avoid dynamic SQL, if at all possible, from a performance standpoint. Please also be aware that you are much more open to SQL injection and should use bind variables and DBMS_ASSERT to help guard against it.
If you run the program multiple time you will get an error even after modifying the program to run the select statement as dynamic SQL or using a returning into clause.
Because when you run the program first time it will create the table without any issue but when you run it next time as the table already created first time and you don't have a drop statement it will cause an error: "Table already exists in the Database".
So my suggestion is before creating a table in a pl/sql program always check if there is any table with the same name already exists in the database or not. You can do this check using a Data dictionary views /system tables which store the metadata depending on your database type.
For Example in Oracle you can use following views to decide if a tables needs to be created or not:
DBA_TABLES ,
ALL_TABLES,
USER_TABLES

In PL/SQL, can I pass the table schema of a cursor FROM clause via a stored procedure parameter?

In PL/SQL, I would like to pass in a "source" schema as a parameter to a stored procedure. For instance:
BEGIN
CURSOR my_cursor IS
SELECT my_field FROM <schema>.my_table
...
I want the 'schema' value to come from an input parameter into the stored procedure. Does anyone know how I could do that?
P.S. Sorry if this is a stupid simple question, but I'm new to PL/SQL and must get some functions written quickly.
In addition to what Mark Brady said, another dynamic SQL option is to use a REF CURSOR. Since your sample code includes a cursor this would be the most relevant.
PROCEDURE select_from_schema( the_schema VARCHAR2)
IS
TYPE my_cursor_type IS REF CURSOR;
my_cursor my_cursor_type;
BEGIN
OPEN my_cursor FOR 'SELECT my_field FROM '||the_schema||'.my_table';
-- Do your FETCHes just as with a normal cursor
CLOSE my_cursor;
END;
This has to be done with dynamic sql.
Either the DBMS_SQL package or the Execute Immediate statement.
You can't use variables in the FROM clause.
A potential solution may be to
ALTER SESSION SET Current_Schema = '' <-- the schema you want.
That command changes the default schema. SO if you have a bunch of identically named tables you can save yourself dynamic SQL and make a Dynamic Alter Session.

Resources