How can i get current username in PL/SQL? - oracle

i have some code
create or replace function policy_test (p_schema varchar2, p_object varchar2)
return varchar2 is
v VARCHAR2(30);
begin
v := USER;
return 'name = ' || v;
end;
Begin
DBMS_RLS.add_policy (
object_schema => 'system',
object_name => 'WORKMAN',
policy_name => 'WORKMAN_policy_test2',
function_schema => 'system',
policy_function => 'policy_test',
statement_types => 'select',
update_check => true
);
End;
and i'd like to ruturn 'name=system' or 'name=Jack' from function policy_test, but i get some error :
[28113] ORA-28113 policy predicate has error
How can i get current user name in policy_test and return sting like 'name=Jack' ?

Your particular VPD policy will append the return value to an implicit WHERE clause. The problem lies in your desired behavior. You stated, "and i'd like to return 'name=system' or 'name=Jack' from function policy_test"
If you were to write out an SQL statement like this: SELECT * FROM mytable WHERE name=system or SELECT * FROM mytable WHERE name=Jack, what would happen? The query will fail every time. When doing string comparison in a WHERE clause, you must enclose the string literal with single tick marks. Change it to WHERE name = 'Jack' and you have valid SQL.
Back to your function. Your function is returning a string, name=Jack and so WHERE name=Jack is what Oracle is generating. You haven't quoted the string literal so the SQL fails. The nature of VPD hides the exception raised (very frustrating) but Oracle does log it in the trace file.

Related

Failed to execute policy function in Oracle. Function in policy has error when i assign variable by "Select into"

If was ok when i create a function and add a policy in Oracle. Error happen when i want to select a table (OBJECT_NAME). It seems like error happen in the select into variable, but i dont know how to fix it (I make sure that select into 1 variable only and the same type as variable)
CREATE OR REPLACE FUNCTION TC6_NHANVIEN(P_SCHEMA VARCHAR2, P_OBJ VARCHAR2)
RETURN VARCHAR2
AS
USERCHECK VARCHAR2(100);
VAITROCHECK VARCHAR(100);
BEGIN
USERCHECK := SYS_CONTEXT('USERENV', 'SESSION_USER');
VAITROCHECK :='';
IF (USER LIKE 'NV%') THEN --i want to check a variable. Error maybe right here
SELECT VAITRO INTO VAITROCHECK
FROM SYSTEM.NHANVIEN
WHERE MANV=USER;
END IF;
IF (USERCHECK LIKE 'NV%'AND VAITROCHECK!='THANHTRA') THEN
RETURN 'MANV = '''||USERCHECK||'''';
ELSE
RETURN '1=1';
END IF;
END;
BEGIN
dbms_rls.add_policy(
OBJECT_SCHEMA => 'SYSTEM',
OBJECT_NAME => 'NHANVIEN',
POLICY_NAME => 'PC_2',
POLICY_FUNCTION => 'TC6_NHANVIEN',
STATEMENT_TYPES => 'SELECT,UPDATE',
UPDATE_CHECK => TRUE
);
END;
grant select on SYSTEM.NHANVIEN to NV001;
select * from SYSTEM.NHANVIEN --LOG IN AS NV001

Sending email based on field name UTL_MAIL.send

How can I send an email based on what is entered into a apex Text field. eg If I enter me#test.com in :p6_supervisor, I would like the email to be sent to that person.
At present I have a preset UTL_MAIL.send which is working.
begin
UTL_MAIL.send(sender => 'test#test.com',
recipients => 'test1#test.com',
subject => 'Test,
message => 'Please Note this is a test' );
end;
But of course its for another purpose, which is sending email to one recipient from a trigger.
Below is the Cursor example
create or replace function "email"
( name_in IN varchar2 )
RETURN number
IS
supervisoremail varchar2(30);
CURSOR c1
IS
select
supervisoremail
from
EMPLOYEE,supervisors
where TO_DATE(contract_start_period,'DD-MM-YYYY') < TO_DATE (SYSDATE,'DD-MM-YYYY') - 275
and (supervisors.supervisorname = employee.supervisorname1
or supervisors.supervisorname = employee.supervisorname2)
and employee_name ='test'
;
BEGIN
OPEN c1;
FETCH c1 INTO supervisoremail;
CLOSE c1;
RETURN supervisoremail;
END;
It'll work, no problem. A simple way is to
create a text item (P6_SUPERVISOR)
create a button which submits the page; you need it so that P6_SUPERVISOR's value is set into the session state
create a process which calls UTL_MAIL; P6_SUPERVISOR item's contents is used as a source for the RECIPIENTS parameter. For example:
UTL_MAIL.send (sender => 'test#test.com',
recipients => :P6_SUPERVISOR,
subject => 'Test message - subject',
MESSAGE => 'Test message - message body');
[EDIT: how to send mails in a loop?]
I'm not sure what you meant by creating a function; it returns just one e-mail address that, probably, belongs to name passed through the NAME_IN parameter (but you used the 'test' name in cursor query).
However, you specified that the function returns a NUMBER, while variable you're selecting the result into is a VARCHAR2. So, which one of these is true?
If you insist on a function, don't enclose its name into double quotes as you'll always have to reference it that way (double quotes, correct upper/lower/mixed case).
Furthermore, what is contract_start_period column's datatype? If it is DATE, don't TO_DATE it. The same goes for SYSDATE - it is a function that returns DATE datatype anyway, so it is wrong to convert it to date once again.
Here's an example which uses a cursor FOR loop (as it is easier to maintain) and sends mail to all supervisoremail addresses returned by that SELECT statement.
begin
for cur_r in (select supervisoremail
from employee e join supervisors s on s.supervisorname = e.supervisorname1
or s.supervisorname = e.supervisorname2
where contract_start_period < sysdate - 275
and e.employee_name = 'test'
)
loop
utl_mail.send(sender => 'test#test.com',
recipients => cur_r.supervisoremail,
subject => 'Test message - subject',
message => 'Test message - message body');
end loop;
end;

Oracle PL/SQL dynamically create name for Procedure and Policy

I'm trying to see if the following would be possible.
it all hinges on creating Policies and Procedures for each row..
Is it possible to create a dynamically named Procedure and pass that name to a dynamically
named policy?
So as following create these in a loop ??
I have an email alert
CREATE OR REPLACE PROCEDURE caps_email_alert (sch varchar2, tab varchar2, pol varchar2)
AS
msg varchar2(20000) := 'CAPS.ME_PAYEE table violation. The time is: ';
BEGIN
msg := msg||to_char(SYSDATE, 'Day DD MON, YYYY HH24:MI:SS');
UTL_MAIL.SEND (
sender => 'me#somewhere.com',
recipients => 'me#somewhere.com',
subject => 'Table modification on caps.me_payee',
message => msg);
END caps_email_alert;
/
and a policy that will call it...
BEGIN
DBMS_FGA.ADD_POLICY (
object_schema => 'CAPS',
object_name => 'ME_PAYEE',
policy_name => 'CHK_CAPS_ME_PAYEE',
audit_column => 'CARRIER_NO',
audit_condition => 'CARRIER_NO = ''20'' ',
handler_schema => 'SYSADMIN_FGA',
handler_module => 'CAPS_EMAIL_ALERT',
enable => TRUE,
statement_types => 'SELECT, UPDATE',
audit_trail => DBMS_FGA.DB + DBMS_FGA.EXTENDED);
END;
/
I would need to write a subprogram that would execute as follows...
( to create a Procedure and Policy for each row )
BEGIN
FOR c IN (SELECT schema as sch, table as tab, cond as pred FROM slac_red_table) LOOP
--here need to create dynamically named procedure and pass it to a dynamically named policy
--so a function that creates a dynamically named procedure
emailerProcedureName emailerProcedureName%TYPE := emailerFunction(c.sch, c.tab)
createPolicyFunction(c.sch, c.tab, c.cond,emailerProcedureName)
END LOOP;
END;
Maybe I can accomplish the same some other way... have you encountered something similar ?

How to validate record type as function input in PL/SQL

I am using Oracle 11g in Windows environment. I am not a thorough PL/SQL Developer. My situation is like this.
I am using a package, need to validate the logging in user. Not checking table column directly to do this.
create or replace package Configuration_pkg as
TYPE user_rec IS RECORD
(email VARCHAR2(120),
password VARCHAR2(120));
TYPE user_tab IS TABLE OF user_rec INDEX BY BINARY_INTEGER;
function Validate_logged_user (p_user_tab IN user_tab) RETURN VARCHAR2;
end Configuration_pkg;
create or replace package body Configuration_pkg as
function Validate_logged_user (p_user_tab IN user_tab) RETURN VARCHAR2 IS
Ismatching number;
begin
select count(1)
into Ismatching
from CG_M_USERS
where username = user_tab.email
and password = user_tab.password;
if Ismatching = 0 then
return 'Invalid username / password';
elsif Ismatching = 1 then
return 'Login successful';
end if;
end Validate_logged_user;
end Configuration_pkg;
I am getting the following error
Error(10,20): PL/SQL: ORA-00904: "USER_TAB"."PASSWORD": invalid identifier
Error(10,29): PLS-00302: component 'PASSWORD' must be declared
I want to validate the user with the value passed with the record type, not directly checking username and password from table. Everyone's help is highly appreciated.
There are few mistakes in your code.
1) You are using a Oracle reserve keyword 'PASSWORD'.
2) You are passing a collection to the function. So you need to run a loop to get the values of the collection.
See the revised complied code.
CREATE OR REPLACE PACKAGE Configuration_pkg
AS
TYPE user_rec IS RECORD
(
email VARCHAR2 (120),
passwrd VARCHAR2 (120)
);
TYPE user_tab IS TABLE OF user_rec INDEX BY BINARY_INTEGER;
FUNCTION Validate_logged_user (p_user_tab IN user_tab)
RETURN VARCHAR2;
END Configuration_pkg;
----------------------------------------
/
CREATE OR REPLACE PACKAGE BODY Configuration_pkg
IS
FUNCTION Validate_logged_user (p_user_tab IN user_tab)
RETURN VARCHAR2
IS
Ismatching NUMBER;
msg1 varchar2(20):= 'Invalid username/passwrd';
msg2 varchar2(20):= 'Login successful';
BEGIN
for r in 1..p_user_tab.count
loop
SELECT COUNT (1)
INTO Ismatching
FROM CG_M_USERS
WHERE username = p_user_tab(r).email
AND passwrd = p_user_tab(r).passwrd;
IF Ismatching = 0
THEN
RETURN msg1;
ELSIF Ismatching = 1
THEN
RETURN msg2;
END IF;
end loop;
END Validate_logged_user;
END Configuration_pkg;

Oracle - change columns in WHERE clause based on input

I want use separate columns in WHERE clause based on the INPUT received in the stored procedure.
If TYPE_DEFINITION = 'SUP' then use SUPPLIER column
If TYPE_DEFINITION = 'CAT' then use CATEGORY column
I know I can write two separate SELECT's using a CASE statement, but that will be very dumb and redundant. Any cleaner way of doing it?
CREATE OR REPLACE PROCEDURE SG.STORED_PROCEDURE (
TYPE_DEFINITION IN VARCHAR2,
VALUE IN VARCHAR2,
STORELIST IN VARCHAR2)
AS
BEGIN
SELECT O.ORGNUMBER,
S.SKU,
FROM SKU S JOIN ORG O ON S.ORGID = O.ORGID
WHERE
AND O.ORGNUMBER IN (STORELIST)
AND (CASE TYPE_DEFINITION
WHEN 'SUP' THEN S.SUPPLIER = VALUE
ELSE S.CATEGORY = VALUE
END);
END;
/
Your code is very close. The CASE THEN must return an expression, not a condition. But the CASE can be used as part of a condition, just move the = VALUE
to the outside.
Change this:
AND (CASE TYPE_DEFINITION
WHEN 'SUP' THEN S.SUPPLIER = VALUE
ELSE S.CATEGORY = VALUE
END);
To This:
AND VALUE = (CASE TYPE_DEFINITION
WHEN 'SUP' THEN S.SUPPLIER
ELSE S.CATEGORY
END);
Your code makes sense. This limitation is probably a result of Oracle not fully supporting Booleans.
UPDATE
If you run into performance problems you may want to use dynamic SQL or ensure that the static SQL is correctly using FILTER operations. When Oracle builds an execution plan it is able to use bind variables like constants, and choose a different plan based on the input. As Ben pointed out, these FILTER operations don't always work perfectly, sometimes it may help if you use simplified conditions like this:
(TYPE_DEFINITION = 'SUP' AND S.SUPPLIER = VALUE)
OR
((TYPE_DEFINITION <> 'SUP' OR TYPE_DEFINITION IS NULL) AND S.CATEGORY = VALUE)
You need to use dynamic sql in your procedure.
Something like this:
CREATE OR REPLACE PROCEDURE SG.STORE_PROC (
TYPE_DEFINITION IN VARCHAR2,
VALUE IN VARCHAR2,
STORELIST IN VARCHAR2)
AS
TYPE EmpCurTyp IS REF CURSOR;
v_emp_cursor EmpCurTyp;
v_stmt_str VARCHAR2(200);
v_orgnumber VARCHAR2(200);
v_sku VARCHAR2(200);
BEGIN
v_stmt_str := 'SELECT O.ORGNUMBER, S.SKU,FROM SKU S JOIN ORG O ON S.ORGID = O.ORGID ';
if type_definition = 'SUP' then
v_stmt_str := v_stmt_str || 'WHERE s.supplier = :v';
else
v_stmt_str := v_stmt_str || 'WHERE s.category = :v';
end if;
-- Open cursor & specify bind variable in USING clause:
OPEN v_emp_cursor FOR v_stmt_str USING value;
-- Fetch rows from result set one at a time:
LOOP
FETCH v_emp_cursor INTO v_orgnumber, v_sku;
-- you can do something here with your values
EXIT WHEN v_emp_cursor%NOTFOUND;
END LOOP;
-- Close cursor:
CLOSE v_emp_cursor;
END;
/

Resources