Debugging PL/SQL Procedure for reservation service report - oracle

For a school project, unable to figure out how to debug the code, I've managed to figure out that the problem is in this line of code: Reservation_id:=: reservation_ID; but I am unsure how to fix it
Set serveroutput ON;
Create or replace procedure Reservation_Services_Report (reservation_ID IN number)
As
Service_number reservation.service_type_id%type;
People_attending reservation.numb_people_attend%type;
Begin
Reservation_id:=: reservation_ID;
Select s.service_name, s.service_type, s.service_type_food, s.service_type_entertainment, r.numb_people_attend from services s, reservation r
where services.service_type_id = reservation.service_type_id;
Exception
When no_data_found then
dbm_output.put_line(‘No services for this reservation’);
End;

Oh, it only it were your only problem!
as you declared an IN parameter, why don't you use it? It is here to be passed to the procedure, not to accept it at runtime (which is what you planned to do with preceding its name with a colon).
besides, it is a good idea to distinguish parameter name from column name, otherwise you'll have problems as Oracle won't know which is which. That's why I renamed it to par_reservation_id
select in PL/SQL requires into clause. In your case, you need to declare all those variables which match select column list. I named them all with the l_ prefix (as a l_ocal variable); some people use v_; pick whichever you want, just try to stick to some standards - it'll make your code easier to read, debug and maintain
select you wrote would probably return too_many_rows error as there's no restriction to number of rows; I presume you "forgot" to include the where clause so I put it there; I don't know whether it is correct or not as I don't have your tables. Fix it, if necessary
when joining tables, try to switch to modern ANSI syntax and separate joins from (where) conditions, as I tried
unless you're running that code in a tool that supports dbms_output to be displayed on the screen, you won't see anything. Exception is handled (kind of), but nobody will know what happened. Consider raising the error instead, e.g. raise_application_error(-20000, 'No services for this reservation')
Here's how the procedure might look like; hopefully, it is somewhat better than the original version. See if it helps.
Create or replace procedure
Reservation_Services_Report (par_reservation_ID IN number)
As
l_service_name services.service_name%type;
l_service_type services.service_type%type;
l_service_type_Food services.service_type_Food%type;
l_service_type_entertainment services.service_type_entertainment%type;
l_numb_people_attend reservation.numb_people_attend%type;
Begin
Select s.service_name,
s.service_type,
s.service_type_food,
s.service_type_entertainment,
r.numb_people_attend
into l_service_name,
l_service_type,
l_service_type_food,
l_service_type_entertainment,
l_numb_people_attend
from services s join reservation r on s.service_type_id = r.service_type_id
where r.reservation_id = par_reservation_id;
Exception
When no_data_found then
dbms_output.put_line(‘No services for this reservation’);
End;

Related

Using regexp_replace to prevent SQL injection

We have thousands of oracle packages that contain a map_products procedure.
We have a table that stores the list of oracle packages a customer would like that map_products run for.
The process that runs them uses dynamic SQL like this:
select sanitize(package_name)
into v_package_name
from custom_plugins
where id = p_id;
execute immediate '
begin
'||v_package_name||'.map_products;
end;
';
The sanitize function above is meant to prevent SQL injection.
Here is the function definition:
function sanitize(p_string in varchar2) return varchar2
is
begin
return regexp_replace(upper(p_string),
'(ALTER|MERGE|CREATE|SELECT|INSERT|UPDATE|DELETE|MODIFY|DROP|ENABLE|DISABLE|;)');
end;
Now we realize this is a dangerous approach in the long run and are planning to redo the entire process. However, for the time being, is there any easy way that this regexp_replace can be circumvented allowing SQL injection?
More specifically, we want to make sure that a semicolon cannot be passed in. Does the above ensure that?
Use the string:
DRDROPOP your_package_name
The replace will only replace DROP once leaving you with:
DROP your_package_name
I suggest whitelisting instead of regular expressions.
Check the input against the system tables.
select object_name
from dba_objects
where owner = 'SYS'
and object_type = 'PACKAGE'
and object_name = :p_string;
If you can't find a match, then it isn't a known package, so don't use it.
I think removing the ; will be a first approach to avoid the sql injection. But please consider this possible strategy:
Extract with regexp a valid package name from the input(for example, checking that you have alphanumeric characters, or "_", or any other character allowed in a package name but nothing else).
With the output from previous step, check that the package is actually an object in the database (querying user_object).
At this point you will have a valid package name (if any) and you could use it in the dynamic statement.
You already said that your approach was dangerous. I just want to make sure that you're aware that it'd be problematic with a package with a name containing any of the words in your regexp (salaryUPDATEr, for example)
As the other posters above have commented blacklisting is going to be quite easy to bypass.
Using whitelist validation is always ideal. If that's not feasible, and since this is Oracle, the best option is to use the built in dbms_assert.enquote_name - this safely enquotes the value (and checks for embedded quotes).
For more info on DBMS_ASSERT see:
https://oracle-base.com/articles/10g/dbms_assert_10gR2#ENQUOTE_NAME
Or for much more in depth on preventing SQL injection see:
http://www.oracle.com/technetwork/database/features/plsql/overview/how-to-write-injection-proof-plsql-1-129572.pdf
You can use the DBMS_ASSERT package.
Since the 10g version, Oracle provides the DBMS_ASSERT package. It contains functions that can be used to validate user input.
Example (SQL Injection attempt)
SELECT description FROM products WHERE name=DBMS_ASSERT.ENQUOTE_LITERAL('abc'' OR 1=1--');
ERROR RETURNED.
ORA-06502: PL/SQL: numeric or value error
You can find more information at link.

Oracle database, pl/sql code verification

I have had the need to verify that a particular pl/sql object (say a procedure) matches between development, test, and production environments. In order to compare and know with 100% certainty that the code matches, I have written a simple function such as this:
function get_plsql_hash (p_owner varchar2, p_name varchar2, p_type varchar2) return number as
v_hash number;
begin
select sum(ora_hash(line||'!'||text)) into v_hash
from dba_source
where owner = p_owner
and name = p_name
and type = p_type;
if v_hash is null then
return 0;
else
return v_hash;
end if;
exception
when others then
return 0;
end get_plsql_hash;
It works great. If I change even just one character of code, I will get a different hash value (checksum or whatever you want to call it). I am able to use Oracle dictionary views to not only verify pl/sql code, but also data in reference/lookup tables, table structure, table privs, indexes, constraints, sequences, etc. The function I pasted above was just one example.
Here is my question.... does something in the Oracle data dictionary already exist that I do not know about that could give me a unique value representing each object? It would be awesome if dba_objects had a column named OBJ_CKSUM or something similar. If Oracle could keep this internally every time an object is modified, it would completely eliminate the need for the code I wrote (which is only a few hundred lines, but zero lines with no code to maintain is far superior). If such a feature does not exist, would Oracle possibly consider adding this in a future release? I think it is very helpful, but I am not sure if other developers, testers, or release management would agree. I do not know how to proceed with requesting a feature from Oracle.

Restricting dbms_sql to SELECT queries

I am writing a function similar to Tom Kyte's print_table, but with more output options and better formatting, etc., etc. It struck me that writing a function where you could enter arbitrary SQL with authid currentuser -- rather than passing a ref cursor -- was pretty dangerous! However, I need to know the column attributes and, AFAIK, there's no other way of getting this.
Is there a way, therefore, of restricting the dbms_sql.execute function so it only operates on select queries? Or, put another way, is there a way to check the type of DML being parsed through the cursor and then, for example, raise an exception if it's anything other than a select?
Textual analysis of the query (e.g., allowing select or with, but nothing else) wouldn't work, because you could just do print_table('select * from dual; drop someTable'); etc...
(P.S., Oracle 10gR2, if it makes a difference.)

ORA-0675:package or function ORG_SPGETTYPE is in an invalid state

PROCEDURE ORG_spGetType
(
v_TypeId IN NUMBER DEFAULT NULL
)
AS
BEGIN
SELECT *
FROM ORG_Type
WHERE TypeId = v_TypeId ;
END;
While Running above proceedure in oracle10g usinq eclipse platform sql editor getting error like "ORA-0675:package or function ORG_SPGETTYPE is in an invalid state"
That's not much of a surprise; that procedure is invalid. When compiling a procedure or any other PL/SQL block you should check whether it's compiled correctly.
If you're compiling on the command line, i.e. in SQL*Plus you can use the SHOW command and use the ERRORS variable, which:
displays the line and column number of the error (LINE/COL) as well as the error itself (ERROR).
After you've compiled the procedure type the following:
show errors
Alternatively, you can use the USER_ERRORS system view which will show you a list of all errors, which the schema you're in can see.
select *
from user_errors
where name = 'ORG_SPGETTYPE'
The actual reason for your error is that you cannot simply SELECT in PL/SQL. If you do you're not giving PL/SQL the ability to use the results of your SELECT statement in any way and so it won't allow you to do so. If you're selecting something you need to do something with it; what that something is is up to you.
There are numerous ways of doing this but let's say the table ORG_TYPE is unique on the column TYPEID and you want to return one column as an OUT parameter. Your procedure would then look like this:
create or replace procedure org_spgettype (
P_typeid in org_type.type_id%type
, P_some_column out org_type.some_column%type
) is
begin
select some_column into P_some_column
from org_type
where typeid = P_typeid;
end;
/
show errors
Please note a number of things:
There doesn't seem to be any need for the DEFAULT NULL if this is your primary key; it can't be NULL so you don't want to allow this possibility.
I've declared your parameters as the type of the column; this enables you to change the column without having to recode everything.
There is so much more to explain about all of this (exceptions for a start) so I would highly recommend taking some basic tutorials first or asking a colleague/friend for help.
You would have a 'compiled with warnings' message when creating the procedure. You can query the user_errors view to see what problems are reported for your peocedure. You will see something like 'PLS-00428: an INTO clause is expected in this SELECT statement', because you are not selecting into something.
The documentation shows how to do that. You need to declare variables to select into. In this case you could declare a rowtype variable:
CREATE OR REPLACE PROCEDURE ORG_spGetType (v_TypeId IN NUMBER DEFAULT NULL) AS
l_org_type org_type%rowtype;
BEGIN
SELECT *
INTO l_org_type
FROM ORG_Type
WHERE TypeId = v_TypeId ;
-- do something with l_org_type
END;
/
Note that this can get a no_data_found exception if there are no matching rows, or too_many_rows if there are multiple matches for the passed ID.
But it isn't clear what you're planning to do with the retrieved data. This currently does nothing at all with it, it's just selected and then forgotten. The name of the procedure suggests you want to return all or part of the data from the table to the caller. If it's only one field value then this should probably be a function rather than a procedure. Or you could add out parameters to put the values into (as Ben shows), or return a refcursor. Depends what you need.
The default null, however, maybe suggests you sometimes expect more than one result, maybe the whole table if no value is passed - though your where clause won't find anything if v_typeid is null.

Quick-n-dirty results: View results of Procedure OUT cursor in SQL Worksheet?

Platform: Oracle
Language: PL/SQL
Issue: Want to output a procedure OUT cursor into the SQLDeveloper SQLWosksheet.
Anyone know how to use the Oracle "Select * from Table( PipelinedFunction( Param ) ) " to check procedure code output cursors?
I am using Crsytal Reports off of an Oracle stored procedure. Crystal requires that a procedure return a cursor, which it fetchs and reads.
The procedure code I have is currently working, but I want to find the easiest way to view the effects of changes to the procedure code. I have SQLDeveloper available, and I'm doing my creation and sql testing in that. I would like to get a quick result visible in the SQL Developer Query Result window ("SQL Worksheet").
Is there a (simple) way to use a Function to read the cursor from the procedure? (and pipe that out to the Table function?)
Convoluted, I know, but I deal best when I can just see the results of code changes. If I can view the record results directly, it will speed up development of the report.
I know of the Table function and a little about pipelining in Oracle. I know a little about cursors in general and sys_refcursor. I know diddly about types and why I need them. (Isn't sys_regCursor supposed to get us away from that?)
The current procedure does an adequate but ungraceful series of queries, inserts to global temp tables (GTT), joins from GTT and original tables, more inserts, and more self-joins and then SELECTS the results into the OUT cursor. I might be able to do better relying on just cursors and such, but the current method is good enough to get results to the report.
I think I can handle SQL pretty well (for our purposes), but I am not an Oracle-specific developer... but I need help.
Anybody run across this? The whole idea was to speed my development for the procedure code, but I've spent a couple of days looking for a way to just get at the output... not what I had in mind.
Update:
I have tried some hare-brained schemes based on slivers that I've seen on the web... such as
Create or replace FUNCTION GET_BACKPLANE (
Node VARCHAR2 ) RETURN SYS_REFCURSOR
AS
RESULTS SYS_REFCURSOR;
BEGIN
Open Results for
Select Backplane(Results, Node) from Dual ;
... etc.
and
Create or replace Function GET_BACKPLANE (
NODE VARCHAR2 ) RETURN My_Table_Stru%ROWTYPE PIPELINED
AS
BEGIN ...
I don't think that Oracle is even considering letting me re-reference the output cursor from the procedure ("Results" is a sys_refcursor that holds the results of the last SELECT in the procedure). I don't know how to define it, open it, and reference it from the procedure.
I never got to the place where I could try
SELECT * FROM TABLE(GET_BACKPLANE( ... etc )
Sorry for any typos and bad Oracle Grammar... it's been a long several days.
SQL Developer allows us to use SQL*Plus commands in the Worksheet. So all you need to do is define a variable to hold the output of the ref cursor.
I may have misinterpreted the actual code you want to run but I'm assuming your actual program is a procedure Backplane(Results, Node) where results is an OUT parameter of datatype sys_refcursor and node is some input parameter.
var rc refcursor
exec Backplane(results=>:rc, Node=>42)
print rc
The output of the print statement is written to the Script Output pane.
Note that the use of SQL*Plus commands means we have to use the Run Script option F5 rather than execute statement.
Thanks for the help. In the end, I wound up brute-force-ing it...
Step by step:
Make a query, test a query,
create a global temp table from the structure,
add code to make another query off of that GTT, test the query,
create a global temp table from the structure,
etc.
In the end, I wound up running (anonymous block) scripts and checking the GTT contents at every stage.
The last part was to use the same last query from the original procedure, stuffing everything into the Cursor that crystal likes...
tomorrow, I test that.
But, I'll just force it through for the next procedure, and get it done in a day and a half instead of 2+ weeks (embarrassed).
Thanks,
Marc

Resources