oracle function not displaying results - oracle

I am very new to this so it might sound basic, however i want create a function that takes two input parameter , (a primary key and a numeric value) and does a calculation to compare if the input numeric value (v_priceWantToInsert) is greater than maximum value in the column of the table and returns the result as true or false.
here is the code :
CREATE OR REPLACE FUNCTION PRICE_CHECK(v_productID IN VARCHAR2, v_priceWantToInsert in NUMBER)
RETURN VARCHAR2
IS
RETURN BOOLEAN AS
v_Bool BOOLEAN;
v_MAXPRICE NUMBER;
BEGIN
SELECT
MAX(B.BIDPRICE) INTO v_MAXPRICE
FROM
PRICEBID B
WHERE B.PRODUCTID = v_productID;
IF (v_MAXPRICE > v_priceWantToInsert) THEN
v_Bool := FALSE;
ELSE
v_Bool := TRUE;
END IF;
END PRICE_CHECK;
DECLARE
var1 VARCHAR2(100);
BEGIN
var1 := case when PRICE_CHECK('P00001',45) then 'true' else 'false' end;
DBMS_OUTPUT.PUT_LINE('It is: ' || var1);
END;
however my code is showing an error that object is invalid AND Usually a PL/SQL compilation error.I am open to any solution as long as it gives me a true or false value.

There are some moments I see in the code:
Missing string's length in DECLARE part. Oracle requires to specify variable's length when it is a "stringy" type, like VARCHAR or CHAR. Limit is 32767:
var1 VARCHAR2(100); -- might be up to 32767
In the function you are using variable v_MAXPRICE which is not defined. It must be done between IS and BEGIN parts of the function's defintion. I think this is how it should look like:
IS
v_MAXPRICE number;
BEGIN
There is another moment you have in your code after you've updated the question
RETURN VARCHAR2
IS
RETURN BOOLEAN AS
v_Bool BOOLEAN;
v_MAXPRICE NUMBER;
Having 2 returns is wrong, replace it with
RETURN BOOLEAN
IS
v_Bool BOOLEAN;
v_MAXPRICE NUMBER;
and since it is a function and has to return something you need to add
return v_bool
after "end if"
I thnk the last part is the anonimous block
first of all, your function returns boolean into var1 which is defined as a varchar2. This is wrong as Oracle can't convert booleans to string and it needs help with that.
try that out:
var1 := case when PRICE_CHECK('P00001',45) then 'true' else 'false' end;

Related

Input sanitization - Numeric values

I've been asked to do input validation in order to prevent sql injection. I've been using dbms assert package functions to do the sanitization. However, when I try to sanitize a number(I'm getting it in varchar2(12 byte)) error is thrown. It's the same case with alphanumeric characters starting with number.
I tried various functions of dbms assert. Nothing seems to work except noop. But, noop is of no use since it does not do any validation.
create or replace procedure employee
(
v_emp_id IN varchar2(12 byte)
)
AS
lv_query CLOB;
BEGIN
if v_emp_id is NOT NULL THEN
lv_query := 'select * from employee where emp_id=''' || dbms_assert.enquote_name(v_emp_id) || '''';
--I also tried below:
-- lv_query := 'select * from employee where emp_id=''' || dbms_assert.simple_sql_name(v_emp_id) || '''';
end if;
END
No source gives more detailed input on dbms_assert package. Please help me in
Whether dbms_assert package can be used to sanitize numeric values(stored in VARCHAR2 variables). If yes, how?
Other ways of sanitizing input. (other than using bind variables)
Thanks.
Oracle 12.2 and higher
If you are on Oracle 12.2 or higher, you can use the VALIDATE_CONVERSION function which would be the simplest solution. Your code could potentially look something like this:
CREATE OR REPLACE PROCEDURE employee (v_emp_id IN VARCHAR2)
AS
lv_query CLOB;
BEGIN
IF v_emp_id IS NOT NULL AND validate_conversion (v_emp_id AS NUMBER) = 1
THEN
lv_query := 'select * from employee where emp_id = ' || v_emp_id;
ELSE
--do something here with an invalid number
null;
END IF;
END;
/
Earlier than Oracle 12.2
If you are not on Oracle 12.2 or higher, you can write your own small function to validate that the value is a number. Using a method similar to what Belayer suggested, just attempt to convert the value to a number using the TO_NUMBER function and if it fails, then you know it's not a number. In my example, I have it as a small anonymous block within the code but you can also make it a standalone function if you wish.
CREATE OR REPLACE PROCEDURE employee (v_emp_id IN VARCHAR2)
AS
lv_query CLOB;
l_is_number BOOLEAN;
BEGIN
--Verify that the parameter is a number
DECLARE
l_test_num NUMBER;
BEGIN
l_test_num := TO_NUMBER (v_emp_id);
l_is_number := TRUE;
EXCEPTION
WHEN VALUE_ERROR
THEN
l_is_number := FALSE;
END;
--Finished verifying if the parameter is a number
IF v_emp_id IS NOT NULL AND l_is_number
THEN
lv_query := 'select * from employee where emp_id = ' || v_emp_id;
ELSE
--do something here with an invalid number
null;
END IF;
END;
/
Well if you cannot change the procedure it means you have no test as that procedure will not compile, so it cannot be executed. However that may be a moot point. You need to define exactly what you mean by "sanitize numeric values". Do you mean validate a string contains a numeric value. If so DBMS_ASSERT will not do that. (Note: The function chooses ENQUOTE_NAME will uppercase the string and put double quotes (") around it thus making it a valid object name.) Further your particular validation may require you define a valid numeric value, is it: an integer, a floating point, is scientific nation permitted, is there a required precision and scale that must be satisfied, etc. As a brute force validation you can simulate the assertion by just convert to number. The following will do that. Like dbms_assert if the assertion is successful it returns the input string. Unlike dbms_assert, however, when the assertion fails it just returns null instead of raising an exception. See fiddle.
create or replace
function assert_is_numeric(value_in varchar2)
return varchar2
is
not_numeric exception;
pragma exception_init (not_numeric,-06502);
l_numeric number;
begin
l_numeric := to_number(value_in);
return value_in;
exception
when not_numeric then
return null;
end assert_is_numeric;

How can I convert an integer variable in PLSQL to pass to a boolean procedure parameter?

I am writing unit tests for an existing procedure which has boolean parameters. As the tests run, they will store the associated parameter values in a table. I want to use variables for setting the parameter columns in the table and to pass to the procedure. Is there a way to go from an integer (what I'll set in my table) to the procedure (which requires boolean values)?
I've tried passing 1/0 values and the strings "TRUE" and "FALSE" to the procedure. I've tried making my table use a boolean datatype for the relevant columns. I've tried CASTing. I've tried using a SELECT INTO with CASE statement to set a different boolean-type variable based on the value of the integer-type variable.
CREATE TABLE UNIT_TEST_RESULTS (
case VARCHAR2(50)
,includeLines NUMBER(1) --this will hold the value of i_includeLines below
,result VARCHAR2(4)
);
CREATE OR REPLACE PROCEDURE X_UNIT_TEST AS
i_includeLines NUMBER(1)
BEGIN
i_includeLines:=0;
X_THING_TO_TEST(includeLinesBool=>i_includeLines);
/*...analyze output...*/
INSERT INTO UNIT_TEST_RESULTS(case,includeLines,result)
VALUES ('test',i_includeLines,'fail'); COMMIT;
END X_UNIT_TEST;
I'd probably do something like
i_includeLines NUMBER(1)
b_includeLines boolean;
BEGIN
i_includeLines:=0;
b_includeLines := (case i_includeLines when 1 then true else false end);
X_THING_TO_TEST(includeLinesBool=>b_includeLines);
or
b_includeLines boolean;
i_includeLines integer;
BEGIN
b_includeLines:= false;
X_THING_TO_TEST(includeLinesBool=>b_includeLines);
/*...analyze output...*/
i_includeLines := case when b_includeLines = true then 1 else 0 end;
INSERT INTO UNIT_TEST_RESULTS(case,includeLines,result)
VALUES ('test',i_includeLines,'fail');
COMMIT;
Obviously, either of these case statements could (and should) get encapsulated into a function assuming you are going to call them regularly.
I would suggest that you not use the reserved word case for a column name-- that's going to bite you at some point. I'm also not a fan of the Hungarian notation for variable names but that's more of a religious debate...
There is no implicit conversion from numbers or strings to Boolean, but a simple true/false expression like i_includeLines = 1 evaluates to Boolean true if i_includeLines has the value 1, so you could call your procedure like this:
x_thing_to_test(includeLinesBool => i_includeLines = 1);
That will pass true if i_includeLines is 1, false if it is not 1, else null.
If you need to treat null as false then you'll need a case/coalesce/nvl or similar expression.
There's no Boolean in your code so - here's how I understood the question:
You have a procedure which accepts parameter that is Boolean:
SQL> create or replace procedure p_test
2 (par_bool in boolean)
3 is
4 begin
5 null;
6 end;
7 /
Procedure created.
SQL>
You want to call it; there's some number variable (l_int in my example) and you'd want to convert it to Boolean and pass as such to the p_test procedure. Here's how:
SQL> declare
2 l_int integer := 1;
3 begin
4 p_test(case when l_int = 1 then true else false end);
5 end;
6 /
PL/SQL procedure successfully completed.
SQL>

PL/SQL Why my function return NULL value instead of a value in 'HH24:MI:SS' format?

I'm trying to code a PL/SQL function to receive by parameter a number in VARCHAR2 type and return a hour in format 'HH24:MI:SS'. eg: If I send by parameter '6', the function will return 06:00:00. The problem is the function returned NULL value but If I write the same process in a simple PL/SQL block it works fine. Do you have any idea?. Thanks :)
CREATE OR REPLACE FUNCTION format_hour_test (
p_hour VARCHAR2)
RETURN VARCHAR2
IS
v_hour_time TIMESTAMP;
v_hour_char VARCHAR2(50);
BEGIN
IF
REGEXP_LIKE(p_hour, '(0-9)') THEN
v_hour_time := TO_TIMESTAMP(p_hour, 'HH24');
END IF;
v_hour_char := TO_CHAR(v_hour_time, 'HH24:MI:SS');
RETURN v_hour_char;
END format_hour_test;
/
SHOW ERRORS;
When I write a anonymus block with the same process this works fine.
SET SERVEROUTPUT ON
DECLARE
v_hour VARCHAR2(20) := '6';
v_hour_time TIMESTAMP := TO_TIMESTAMP(v_hour, 'HH24');
v_result VARCHAR2(20) := TO_CHAR(v_hour_time, 'HH24:MI:SS');
BEGIN
DBMS_OUTPUT.PUT_LINE(v_result);
END;
You have the wrong type of brackets in your regular expression; it should be:
IF REGEXP_LIKE(p_hour, '[0-9]') THEN
You used grouping parentheses, insteaf of matching/class brackets. You would get a match if you passed in the string '0-9', which wouldn't then be converted to a date/time.
Read more.
Your anonymous block is not the same process. In the anonymous block you do not have if regexp_like... In regular expressions, parens group, so you are looking for the string 0-9. So v_hour_time is not being set in the procedure.
I think you are looking for [0-9], which is match one of the characters in the range. Alternatively, [[:digit:]] https://docs.oracle.com/cd/B12037_01/server.101/b10759/ap_posix001.htm#i690819
Your v_hour_time is never initialized, because of your REGEXP_LIKE conditional:
select (case when REGEXP_LIKE('6', '(0-9)') then 'true' else 'false' end) as res from dual;
returns:
RES
-----
false

How to display value return from function in procedure

I want to pass in p_string to the procedure, then the procedure will pass it to the function, and the function will return boolean to the procedure, the boolean return to procedure will then display using dbms_output.put_line. However, i have been having problem displaying it. How should i display it?
CREATE OR REPLACE PACKAGE BODY LAB4_527802_pkg AS
FUNCTION LAB4_527802_FCN( p_string VARCHAR2)
RETURN BOOLEAN
AS
string1 BOOLEAN;
BEGIN
IF p_string = 'AAA' THEN
string1 := TRUE;
ELSE
string1 := FALSE;
END IF;
return string1;
END LAB4_527802_FCN;
PROCEDURE LAB4_527802_PROC (p_string varchar2)
AS
string1 boolean;
BEGIN
string1 := LAB4_527802_pkg.LAB4_527802_FCN(p_string);
dbms_output.put_line (string1);
END LAB4_527802_PROC;
END LAB4_527802_pkg;
/
SHOW ERRORS
This is how i call the procedure.
set serveroutput on;
begin
LAB4_527802_pkg.LAB4_527802_PROC ('AAA');
end;
Although named string1 the variable is declared as boolean, both in function LAB4_527802_FCN and in procedure LAB4_527802_PROC.
I would first suggest using a different name. But as you may imagine, this is not the reason for the problem.
In line
dbms_output.put_line (string1);
you're trying to print a variable of type boolean which doesn't work. Procedure put_line needs a 'string' (char, varchar, varchar2) argument.
I suspect you get the following error, don't you?
PLS-00306: wrong number or types of arguments in call to 'PUT_LINE'
You need to convert the boolean to a string in order to print it. E.g.:
DBMS_OUTPUT.put_line (CASE WHEN string1 THEN 'TRUE' ELSE 'FALSE' END);

Function object is invalid

I was executing query in Oracle Application Express and it was fine. Until I run my application, it gives me error
Error in PLSQL code raised during plug-in processing.
ORA-06550: line 4, column 1: PLS-00905: object
PURCHASEORDER.ACLSTUDENT_CUSTOM_AUTH is invalid ORA-06550: line 4,
column 1: PL/SQL: Statement ignored
and this is my sql
create or replace FUNCTION aclstudent_custom_auth (
p_username IN VARCHAR2(50),
p_password IN VARCHAR2(20))
RETURN boolean IS
valid boolean;
BEGIN
FOR c1 IN (SELECT 1 FROM students
WHERE upper(student_userid) = upper(p_username)
AND upper(student_last_name) = upper(p_password))
LOOP
valid := TRUE;
RETURN valid;
END LOOP;
valid := FALSE;
RETURN valid;
END;
Declaration of input parameters of the function is without length.
CREATE OR REPLACE FUNCTION aclstudent_custom_auth (
p_username IN VARCHAR2,
p_password IN VARCHAR2)
RETURN boolean IS
valid boolean;
BEGIN
FOR c1 IN (SELECT 1 FROM students
WHERE upper(student_userid) = upper(p_username)
AND upper(student_last_name) = upper(p_password))
LOOP
valid := TRUE;
RETURN valid;
END LOOP;
valid := FALSE;
RETURN valid;
END;
This must work better.
As Massie says, don't specify length in your arguments. Also, as hinted by Lalit, your use of a return variable as well as two RETURN statements is superfluous. You should either just RETURN the required BOOLEAN value in one of two places or set the variable and have one RETURN at the end of the function. I'd go with the first approach as your code will be more concise.
Incidentally, I don't like using loops to check for the existence of one record, but that's by the by.
CREATE OR REPLACE FUNCTION aclstudent_custom_auth(p_username IN VARCHAR2,
p_password IN VARCHAR2)
RETURN BOOLEAN IS
BEGIN
FOR c1 IN (SELECT 1
FROM students
WHERE upper(student_userid) = upper(p_username)
AND upper(student_last_name) = upper(p_password)) LOOP
RETURN TRUE;
END LOOP;
RETURN FALSE;
END;
OR
CREATE OR REPLACE FUNCTION aclstudent_custom_auth(p_username IN VARCHAR2,
p_password IN VARCHAR2)
RETURN BOOLEAN IS
valid BOOLEAN;
BEGIN
FOR c1 IN (SELECT 1
FROM students
WHERE upper(student_userid) = upper(p_username)
AND upper(student_last_name) = upper(p_password)) LOOP
valid := TRUE;
END LOOP;
valid := FALSE;
RETURN valid;
END;

Resources