Verify what the user entered - ada

I am a beginner in Ada programming and I have the following code :
PROCEDURE ask(variable: OUT myType) IS
BEGIN
Put("Enter : ");
Get(variable);
Skip_Line;
EXCEPTION
WHEN OTHERS => RAISE wrongInput;
END ask;
This procedure asks to enter something and put it in my "variable" variable (which type is myType containing characters from '1' to '3').
I raise an exception when the input is not correct.
Now I would like to do something else when the input is 'm' for example.
How could I do this?

Change the declaration of myType:
type myType is ('1', '2', '3', 'm');

This answer is based on the assumption that myType is declared as something like
subtype myType is Character range '1' .. '3';
You can write the output into an unconstraint Character variable, then check it:
PROCEDURE ask (variable : OUT myType) IS
Input : Character;
BEGIN
Put ("Enter : ");
Get (Input);
Skip_Line;
CASE Input IS
WHEN 'm' =>
RAISE gotM;
WHEN OTHERS =>
-- this will raise Constraint_Error if the value is not
-- in range '1' .. '3'
variable := Input;
END CASE;
EXCEPTION
WHEN OTHERS => RAISE wrongInput;
END ask;
I used case instead of a simple if because it sounds like there may be other input values added in the future. Now while this is technically possible, I consider it bad style, because it uses the exception gotM for implementing non-exceptional behavior. You should re-think your code layout so that you don't have a procedure ask that can only return '1' .. '3' but needs to handle other input, too.

Something like this, perhaps? (just guessing here, "I would like to do something else" is extremely vague).
procedure Ask (Variable : out My_Type) is
begin
loop
begin
Put ("Enter :");
Get (Variable);
Skip_Line;
return;
exception
when others =>
Skip_Line;
Put_Line ("invalid.");
end;
end loop;
end Ask;

Related

PL/SQL if then else statements not running

I have written following code in oracle pl/sql
create or replace procedure sorting_criteria(criteria in varchar)
as
begin
if(criteria='lowest price')
then
declare
p_name product.p_name%type;
cursor ptr is select p_name from product order by amount ASC;
begin
open ptr;
loop
fetch ptr into p_name;
exit when ptr%notfound;
dbms_output.put_line(p_name);
end loop;
close ptr;
end;
else if(criteria='highest price')
then
declare
p_name product.p_name%type;
cursor ptr is select p_name from product order by amount DESC;
begin
open ptr;
loop
fetch ptr into p_name;
exit when ptr%notfound;
dbms_output.put_line(p_name);
end loop;
close ptr;
end;
else
dbms_output.put_line('Enter valid criteria!');
end if;
end;
/
But it is giving following error: Error at line 35: PLS-00103: Encountered the symbol ";" when expecting one of the following: Please help
The ELSE-IF statement in PL/SQL has to be written as ELSIF. Otherwise, you should close the second IF with an other END IF; statement.
You can solve the issue by changing the ELSE IF at line 17 to an ELSIF
The answer by #GregorioPalamà correctly addresses your issues. But you can drastically reduce the workload by changing your thinking away from "If...then...else" to the "set of" and letting SQL do the work. In this case the only difference is sorting either ascending or descending on amount. The same effect can be achieved by sorting ascending on amount or minus amount; and SQL can make that decision. So you can reduce the procedure to validating the parameter and a single cursor for loop:
create or replace procedure sorting_criteria(criteria in varchar2)
as
cursor ptr(c_sort_criteria varchar2) is
select p_name
from product
order by case when c_sort_criteria = 'lowest price'
then amount
else -amount
end ;
begin
if criteria in ('lowest price', 'highest price')
then
for rec in ptr(criteria)
loop
dbms_output.put_line('Product: ' || rec.p_name );
end loop;
else
dbms_output.put_line('Enter valid criteria!');
end if;
end sorting_criteria;
/
See demo here. For demonstration purposed I added the amount to the dbms_output.
A couple notes:
While it is not incorrect using p_... as a column name, it is also
not a good idea. A very common convention (perhaps almost a
standard) to use p_... to indicate parameters. This easily leads to
confusion; confusion amongst developers is a bad thing.
IMHO it is a bug to name a local variable the same as a table
column name. While the compiler has scoping rules which one to use
it again leads to confusion. The statement "where table.name = name"
is always true, except when at least one of them is null, which possible could lead to updating/deleting every row in your table. In this
case p_name is both a column and a local variable.

Ada - constraint error

I can't figure out why the code below is failing. I have my own version of the same script which returns the same error. I can't figure out in either case why the error persists.
This is the error:
raised CONSTRAINT_ERROR : main2.adb:32 index check failed
which is this line:
temp(i) := a(loop_high);
Anyone know what may be causing this?
with Text_IO;
with Ada.Integer_Text_IO;
procedure main2 is
use Text_IO;
use Ada.Integer_Text_IO;
type int_array is array(1..5) of integer;
tosort:int_array;
procedure merge (a:in out int_array; low,mid,high:in integer) is
temp: int_array;
choose1: boolean;
loop_low,loop_high:integer;
begin
loop_low:=low;
loop_high:=high;
for i in low..high loop
if (loop_low>mid) then choose1:=false;
elsif (loop_high>high) then choose1:=true;
else choose1:= a(loop_low)<a(loop_high);
end if; -- choose which side
if choose1 then -- choose from low side
temp(i):=a(loop_low);
loop_low:=loop_low+1;
else
temp(i):=a(loop_high); -- choose from high side
loop_high:=loop_high+1;
end if;
end loop;
a:=temp;
end merge;
procedure mergesort(a: in out int_array;low,high:integer) is
mid:integer;
begin
if low<high then
mid:= (high+low)/2;
mergesort(a,low,mid);
mergesort(a,mid+1,high);
merge(a,low,mid,high);
end if;
end mergesort;
begin
tosort := (171, 201, 397, 10, -381);
mergesort(tosort,1,5);
end main2;
I recommend compiling with -gnateE, which will give you more information for compiler-generated exceptions. In this specific case, it should tell you which value is outside of which range

Oracle PL/SQL - ORA-01403 “No data found” when using “SELECT INTO”

I have a pl sql code that execute three queries sequentially to determine a match level and do some logic
The issue is - when first query has no results (completely valid scenario) I get ORA-01403 No data found.
I understand that I need to incorporate [ Exception clause when NO_DATA_FOUND ]- but how to add it and continue to the next query?
PL/SQL Code
SELECT A into PARAM A FROM SAMPLE WHERE SOME CONDITION;
-- GOT ORA-01403 No data found HERE
MATCH_LEVEL =1;
if A is null then
do some logic;
end if
SELECT A INTO PARAM_B FROM SAMPLE WHERE SOME OTHER CONDITION
MATCH_LEVEL =2
if A is null then
do some logic 2;
end if
SELECT A INTO PARAM_B FROM SAMPLE WHERE SOME OTHER CONDITION
MATCH_LEVEL =3
if A is null then
do some logic 3;
end if
END PL/SQL Code
Declare
--your declarations
begin
SELECT A into PARAM A FROM SAMPLE WHERE SOME CONDITION;
-- GOT ORA-01403 No data found HERE
Begin
MATCH_LEVEL =1;
if A is null then
do some logic;
end if;
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line ('Error...');
END;
--- and son on for other blocks
end;
Just surround your SELECT INTO with begin-end;
begin
-- your faulty statement here
Exception
When NO_DATA_FOUND Then
-- Do what you want or nothing
WHEN TOO_MANY_ROWS THEN
-- what if you get more then one row? and need specific handler for this
When OTHERS Then
-- do something here or nothing (optional - may happen if you have more than your SELECT INTO between 'begin' and 'Exception')
end;
This is like try block of PL/Sql
With this technique you can log the reason your statement failed.
For a SELECT ... INTO ... statement, the PL/SQL engine assume there will be one, and only one row returned by your query. If there is no row, or more than one, an exception is raised.
FWIW, you can handle such cases without resorting on exception handling by using aggregate functions. That way, there will always be only one row in the result set.
Assuming A can't be NULL in your rows:
SELECT MAX(A) into PARAM A FROM SAMPLE WHERE SOME CONDITION;
-- A would be NULL if there was *no* row. Otherwise, it is *the* value for *the* row
MATCH_LEVEL =1;
if A is null then
do some logic;
end if
If the NULL value is a possible case, just add an extra COUNT(*) column:
SELECT MAX(A), COUNT(*) into A, HAS_FOUND_ROW FROM SAMPLE WHERE SOME CONDITION;
if HAS_FOUND_ROW > 0 then
...
end if;
Oracle will not allow you to open an implicit cursor (i.e. a select statement in the body of a code block) that returns no rows. You have two options here (3 really, counting #Sylvain's answer, but that is an unusual approach): use an explicit cursor or handle the error.
Explicit Cursor
An explicit cursor is one found in the DECLARE section it must be opened and fetched manually (or in a FOR loop). This has the added advantage that, if you parameterize the query properly, you can write it once and use it multiple times.
DECLARE
a sample.a%type;
MATCH_LEVEL number;
cursor cur_params (some_column_value number) is
SELECT A FROM SAMPLE WHERE some_column = some_column_value;
BEGIN
MATCH_LEVEL := 1;
open cur_params (match_level);
fetch cur_params into a;
close cur_params;
if A is null then
null; --some logic goes here
end if;
MATCH_LEVEL := 2;
open cur_params (match_level);
fetch cur_params into a;
close cur_params;
if A is null then
null; --some logic goes here
end if;
end;
Handle the error
If you choose to handle the error, you'll need to create a BEGIN...END block around the code that is going to throw the error. When disregarding an error, it's crucial that you ensure that you are only disregarding the specific error you want avoid, when generated from the specific statement you expect it from. If you simply add the EXCEPTION section to your existing BEGIN...END block, for instance, you couldn't know which statement generated it, or even if it was really the error you expected.
DECLARE
a sample.a%type;
MATCH_LEVEL number;
BEGIN
MATCH_LEVEL := 1;
BEGIN
SELECT A into A FROM SAMPLE WHERE some_column = MATCH_LEVEL;
EXCEPTION
WHEN NO_DATA_FOUND THEN
null; --Do nothing
END;
if A is null then
null; --some logic goes here
end if;
MATCH_LEVEL := 2;
BEGIN
SELECT A into A FROM SAMPLE WHERE some_column = MATCH_LEVEL;
EXCEPTION
WHEN NO_DATA_FOUND THEN
null; --Do nothing
END;
if A is null then
null; --some logic goes here
end if;
end;
While I'd discourage it, you can catch any other errors in the same exception blocks. However, by definition, those errors would be unexpected, so it would be a poor practice to discard them (you'll never know they even happened!). Generally speaking, if you use a WHEN OTHERS clause in your exception handling, that clause should always conclude with RAISE;, so that the error gets passed up to the next level and is not lost.

Check a record IS NOT NULL in plsql

I have a function which would return a record with type my_table%ROWTYPE, and in the caller, I could check if the returned record is null, but PL/SQL complains the if-statement that
PLS-00306: wrong number or types of arguments in call to 'IS NOT NULL'
Here is my code:
v_record my_table%ROWTYPE;
v_row_id my_table.row_id%TYPE := 123456;
begin
v_record := myfunction(v_row_id)
if (v_record is not null) then
-- do something
end if;
end;
function myfunction(p_row_id in my_table.row_id%TYPE) return my_table%ROWTYPE is
v_record_out my_table%ROWTYPE := null;
begin
select * into v_record_out from my_table
where row_id = p_row_id;
return v_record_out;
end myfunction;
Thanks.
As far as I know, it's not possible. Checking the PRIMARY KEY or a NOT NULL column should be sufficient though.
You can check for v_record.row_id IS NULL.
Your function would throw a NO_DATA_FOUND exception though, when no record is found.
You can't test for the non-existence of this variable so there are two ways to go about it. Check for the existence of a single element. I don't like this as it means if anything changes your code no longer works. Instead why not just raise an exception when there's no data there:
I realise that the others in the exception is highly naughty but it'll only really catch my table disappearing when it shouldn't and nothing else.
v_record my_table%ROWTYPE;
v_row_id my_table.row_id%TYPE := 123456;
begin
v_record := myfunction(v_row_id)
exception when others then
-- do something
end;
function myfunction(p_row_id in my_table.row_id%TYPE) return my_table%ROWTYPE is
v_record_out my_table%ROWTYPE := null;
cursor c_record_out(c_row_id char) is
select *
from my_table
where row_id = p_row_id;
begin
open c_record_out(p_row_id);
fetch c_record_out into v_record_out;
if c_record_out%NOTFOUND then
raise_application_error(-20001,'no data);
end if;
close c_record_out;
return v_record_out;
end myfunction;

Getting 'Invalid Argument' error in PL/SQL block

The following PL/SQL will not execute and simply returns 'Invalid Argument' as an error. I've gone as far as commenting out eavery line one by one and I can vary the error I receive but I cannot get the proc to run as I cannot identify where the error lies and it may well lie in permissions or the executing user but any help will be received gratefully. Here's the proc
DECLARE
ind NUMBER; -- Loop index
h1 NUMBER; -- Data Pump job handle
percent_done NUMBER; -- Percentage of job complete
job_state VARCHAR2(30); -- To keep track of job state
le ku$_LogEntry; -- For WIP and error messages
js ku$_JobStatus; -- The job status from get_status
jd ku$_JobDesc; -- The job description from get_status
sts ku$_Status; -- The status object returned by get_status
BEGIN
h1 := DBMS_DATAPUMP.OPEN('EXPORT','SCHEMA',NULL,'SQL_INSTALLER_01152009_1014','LATEST');
DBMS_DATAPUMP.ADD_FILE(h1,'SQL_INSTALLER_01152009_1014.dmp','ORACLE_SCRIPT_RUNNER_BACKUP',NULL,1);
DBMS_DATAPUMP.ADD_FILE(h1,'SQL_INSTALLER_01152009_1014.log','ORACLE_SCRIPT_RUNNER_BACKUP',NULL,3);
DBMS_DATAPUMP.METADATA_FILTER(h1, 'SCHEMA_LIST','''speccs_web_test''', NULL, NULL);
DBMS_DATAPUMP.START_JOB(h1);
percent_done := 0;
job_state := 'UNDEFINED';
while (job_state != 'COMPLETED') and (job_state != 'STOPPED') loop
dbms_datapump.get_status(
h1,
dbms_datapump.ku$_status_job_error +
dbms_datapump.ku$_status_job_status +
dbms_datapump.ku$_status_wip,
-1,
job_state,
sts);
js := sts.job_status;
if js.percent_done != percent_done then
percent_done := js.percent_done;
end if;
if (bitand(sts.mask,dbms_datapump.ku$_status_wip) != 0) then
le := sts.wip;
else
if (bitand(sts.mask,dbms_datapump.ku$_status_job_error) != 0) then
le := sts.error;
else
le := null;
end if;
end if;
if le is not null then
ind := le.FIRST;
while ind is not null loop
ind := le.NEXT(ind);
end loop;
end if;
end loop;
dbms_datapump.detach(h1);
END;
I found something:
See here: http://petermag.blogspot.com/2008/01/export-datapump-how-to-do-via-plsql.html
You get an invalid argument error error when the dump file already exists.
You can download SQL Developer and use its debugger (it is free).
See here: http://www.oracle.com/technology/software/products/sql/index.html
Thanks there chap. I have continued debugging and have nailed it down to one line..
DBMS_DATAPUMP.METADATA_FILTER(h1, 'SCHEMA_LIST','''speccs_web_test''', NULL, NULL);
This is the one. Comment it out and all is swell. The error is indicating one of these values is not valid and I would hazard a guess it's the odd looking '''speccs_web_test''' but searches seem to indicate this is correct, this is because that parameter is a list and haven can be '(''name'', ''name''); which translates to ('name', 'name') a valid format for an IN list.
I'll probably sort it in a minute or two but you think i'd of nailed it hours ago :)
For this particular problem it is because the schema_name speccs_web_test is not in upper case. SPECCS_WEB_TEST is fine and the proc is now running ;)

Resources