pass session user value to procedure - sql

I have created a procedure in user A to get the list of tables belonging to session user (who ever runs the procedure) and insert into a table. but when I execute the procedure from user B. It is not getting the values, I mean the session_user value is not being passed to procedure ?? Ienter code heres this an expected behavior ? Is there a way I can pass the session user without actually passing it while execution of procedure(it needs to it by itself) ?
Table :
CREATE TABLE "ABCD"
( "USERNAME" VARCHAR2(1`enter code here`00 BYTE),
"TABLE_NAME" VARCHAR2(100 BYTE)) ;
Procedure I am trying to run :
CREATE OR REPLACE PROCEDURE "SESS_USR_OBJECTS" AS
V_USER varchar2(50):= SYS_CONTEXT('USERENV','SESSION_USER');
cursor SEL_CUR is select OWNER,TABLE_NAME from all_tables where owner in (V_USER);
SEL_REC SEL_CUR%ROWTYPE;
VSQL1 VARCHAR2(100);
BEGIN
OPEN SEL_CUR;
LOOP
FETCH SEL_CUR INTO SEL_REC;
EXIT WHEN SEL_CUR%NOTFOUND;
VSQL1:= 'insert into ABCD VALUES ('''||SEL_REC.OWNER||''','''||SEL_REC.TABLE_NAME||''')';
Dbms_Output.Put_Line(VSQL1);
EXECUTE IMMEDIATE VSQL1;
COMMIT;
END LOOP;
CLOSE SEL_CUR;
END;
When I runt the procedure nothing is being inserted into table.
Yes, user B has access to run the procedure in A.

You are selecting from ALL_TABLES, which is a view that is filtered by SCHEMAID and other (internal) representations of the current user, not SESSION_USER. So, when you execute the procedure, you are selecting from a list of tables that user "A" can see but that are owned by user "B". Apparently, that list is empty in your database.
To make this work the way you want, you need to use invoker's rights on that procedure, like this:
CREATE OR REPLACE PROCEDURE "SESS_USR_OBJECTS" AUTHID CURRENT_USER AS...
That way, when it runs, it will select from user "B"'s version of ALL_TABLES.

Related

Trigger for the nexval in a sequence returing ORA-01403

I have a page in Apex where the user can connect a row from a table to another (a mail to the address) because we changed how we store addresses so we are connecting the two tables while working on them.
The link between the tables is a row in a third table that has 5 columns (ID||ID_RACC||ID_INDIRIZZO||DATA_INS||USER_INS), where ID is the main ID of the link-table, ID_RACC is the ID of the mail's table and ID_INDIRIZZO is the ID of the addresses' table.
If I try from the shell to run this query:
INSERT INTO INSERT INTO INDIRIZZI_RACCOMANDATE (ID_RACC, ID_INDIRIZZO, USER_INS, DATA_INS)
VALUES (p_id_racc, p_id_indirizzo, v('USER'), SYSDATE);
with p_id_racc, p_id_indirizzo non-empty variables, I don't have any problem.
But if the user select from a Select List the address and click the Save button from a specific page he/she receives
ORA-01403: no data found
the only code that is run from him/her is the above one.
I searched and I found out that the problem could be the trigger that fills the ID column in the table INDIRIZZI_RACCOMANDATE from a sequence.
The trigger code is:
create or replace trigger "BI_INDIRIZZI_RACCOMANDATE"
BEFORE
insert on "INDIRIZZI_RACCOMANDATE"
for each row
begin
if :NEW."ID" is null then
select "INDIRIZZI_RACCOMANDATE_SEQ".nextval into :NEW."ID" from sys.dual;
end if;
end;​
I can't understand how is it possible to have a no_data_found with only a select nextval from a sequence.
Then I can't understand how is it possible that I have this problem only if I run it from that page and don't have it if I run the exact same code from shell.
But if the user select from a Select List the address and click the Save button from a specific page he/she receives ORA-01403: no data found
While in Apex, use :APP_USER:
INSERT INTO INSERT INTO INDIRIZZI_RACCOMANDATE
(ID_RACC, ID_INDIRIZZO, USER_INS, DATA_INS)
VALUES
(p_id_racc, p_id_indirizzo, :APP_USER, SYSDATE);
^^^^^^^^^
this
By the way, v('USER') is suspicious; should have been v('APP_USER'), I presume.
V (an absolutely abhorrent object name Oracle should know better) is defined as a collection indexed by varchar2. Your problem is that 'USER' is not a valid index value for V. When a collection does not contain the referenced index value on a collection Oracle throws NO DATA FOUND. A bad choice but the one they made. That is how you get that error. use v('APP_USER'); See fiddle and below.
declare
type example_att
is table of varchar2(30)
index by varchar2(8);
example_data example_att;
begin
dbms_output.enable;
example_data('A') := 'Abcd';
example_data('E') := 'Efgh';
example_data('I') := 'Ijlk';
dbms_output.put_line( 'value for ''A'' is ' );
dbms_output.put_line( example_data('A') );
-- OOPS
dbms_output.put_line( 'value for ''L'' is ' );
dbms_output.put_line( example_data('L'));
end;
/
I had the same error message for a procedure I would call in a dynamic action. The Procedure takes two arguments where one is the APP_USER.
BOOKMARK_FAVORITE(v('CARD_ID'), v('APP_USER'));
In my Procedure I would then select the User_ID for this specific APP_USER. And here is where the error was. The username I was selecting was not in Uppercase but the APP_USER was (IN_USERNAME). So I had to use the UPPER function:
select au.user_id into IN_USER_ID
from app_user au
where UPPER(au.username) = IN_USERNAME; --IN_USERNAME = APP_USER
^^^^^
Apparantly once the user log's in, the username will be stored in APP_USER in UPPERCASE, irrespective of how user has entered in log-in screen.
Don't know if this will help in your situation though :/

Trigger Oracle - Cant capture the userinformation who change the table

I was trying to capture the user who fires [dml-operation] in the table_name from any schema. While trying to do so when I wrote the following code its captures the wrong osuser.
Code
create ore replace trigger trigger_name
after insert on table_name
for each row
declare
v_username varchar2(20);
v_osuser varchar2(20);
begin
select distinct osuser, username into v_osuser, v_username from v$session where osuser in ( select sys_context('USERENV', 'os_user') from dual;
insert into audit_table values (v_osuser, v_username);
end;
/
How can I modify this code so that I can address/solve this issue?
Note:
I am using the trigger in one server, and calling from the other server. Is there any way we can store the user information of the calling server. currently, it is returning the user information from the trigger defined server.
Thank You.
Try using this code:
select osuser, username
from v$session
where sid=(select sid from v$mystat where rownum=1);
use sys_context('userenv','CURRENT_SCHEMA') and sys_context('userenv','OS_USER')
to get the schema name/os user. No need to do any "select into" or declare any local variables
CREATE OR REPLACE TRIGGER trigger_name AFTER
INSERT ON table_name
FOR EACH ROW
BEGIN
INSERT INTO audit_table VALUES (
sys_context(
'userenv','OS_USER'
),
sys_context(
'userenv','CURRENT_SCHEMA'
)
);
END trigger_name;
/
Might be worth reading the docs on sys_context
When an operation is executed over a db link, the "user" is the user defined in the db link, not the user that is invoking the db link. For instance, database MYDB
CREATE PUBLIC DATABASE LINK "MYLINK"
CONNECT TO "SCOTT" IDENTIFIED BY "TIGER"
USING 'YOURDB';
Now, when user FRED executes the following:
select empno from emp#mylink;
Then, in database 'YOURDB', the operation is being executed by YOURDB's user SCOTT, not by MYDB's user FRED.

ORACLE PLSQL Create user with trigger identified by :new username and password

I'm writing an online videogame Database in SQL using ORACLE for an accademic project, and i'm trying to create a trigger that for every user that submit their information in my ACCCOUNT TABLE
CREATE TABLE ACCOUNT (
USERNAME VARCHAR(20),
PASSWORD VARCHAR(20) NOT NULL,
NATIONALITY VARCHAR(15),
CREATION DATE DATE,
EMAIL_ACCOUNT VARCHAR(35) NOT NULL,
CONSTRAINT KEYACCOUNT PRIMARY KEY(USERNAME),
CONSTRAINT NO_USER_CSPEC CHECK(REGEXP_LIKE(USERNAME, '^[a-zA-Z0-9._]+$') AND USERNAME NOT LIKE '% %'),
CONSTRAINT NO_EASY_PASS CHECK(REGEXP_LIKE(PASSWORD, '^[a-zA-Z0-9._!#£$%&/()=?]') AND PASSWORD NOT LIKE '% %'),
CONSTRAINT LENGHTUSER CHECK(LENGTH(USERNAME)>3),
CONSTRAINT LENGHTPASS CHECK(LENGTH(PASSWORD)>5),
CONSTRAINT FK_EMAIL FOREIGN KEY(EMAIL_ACCOUNT) REFERENCES PERSONA(EMAIL) ON DELETE CASCADE
);
Will fire a trigger that will create a new user with the new username and password just inserted.
this is the code i tried to wrote
CREATE OR REPLACE TRIGGER NEW_USER
AFTER INSERT ON ACCOUNT
FOR EACH ROW
BEGIN
CREATE USER :NEW.USERNAME IDENTIFIED BY :NEW.PASSWORD;
GRANT ROLE_NAME TO :NEW.USERNAME
END;
Why i'm tyring to do this ?
Basically because i'd like to give specific view on specific row that regards only the specific user. ( imagine if, while managing your account you can access to every other row stored in the table ACCOUNT )
After creating that specific user i can create some procedure that have in input the username ( of a successfully created user ) and give back the view on that specific row.
is there a way to do this ?
At first, you can't use DDL statement in trigger body as a open source, you should put it in execute immediate command. And also you should pay attention to user privileges which will execute then trigger, and role which will be granted to user, are there all priveleges granted, for create session, execute statements and so on. But if I were you I'll put user opening process in separate procedure, I think it won't be so simple code, so it will be easy to edit package procedure.
You can create context for you user sessions, wrap all your table where you want to control access into views and then filter view by user context.
For example you table TAB_A with many rows, in table you store column ACS_USER and wrap table to V_TAB_A , when you can control access to table via view, all user access object will use views like
select * from V_TAB_A where ACSUSER = SYS_CONTEXT('USERENV','SESSION_USER')
The main problem I see here is grant to create user. You probably don't want your schema to be able to create users. So trigger (of course as other answers states this need to be execute immediate) shouldn't directly call create user. I would create procedure that create user in other schema than your working schema. That external schema would have grants to create user and your schema would have only grant to execute that one procedure from strong priviledged schema. In that case trigger will only call single procedure from external schema.
So to recap:
CREATE OR REPLACE TRIGGER your_schema.NEW_USER
AFTER INSERT ON ACCOUNT
FOR EACH ROW
BEGIN
STRONG.CREATE_USER(:NEW.PASSWORD,:NEW.USERNAME);
END;
CREATE OR REPLACE PROCEDURE STRONG.CREATE_USER(PASS VARCHAR2, USERNAME VARCHAR2) AS
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
execute immediate 'CREATE USER ' || USERNAME || ' IDENTIFIED BY ' || PASS;
execute immediate 'GRANT ROLE_NAME, CONNECT, RESOURCE TO ' || USERNAME; --and whatever user needs more
END;
Where STRONG user have rights to create user and your_schema has grant to execute STRONG.CREATE_USER
Additional thing. Never store passwords in plain text. Use some hash.
Alternatively, you can use a database stored procedure instead of a trigger to do the DDL operations.
This is a pseudo code, make necessary changes as per your requirement. You can build your logic on top of this and if you are stuck, always post a question here in SO.
Table
CREATE TABLE account_info
(
user_name VARCHAR (20),
user_password VARCHAR (20) NOT NULL
);
Procedure
CREATE OR REPLACE PROCEDURE test_procedure (
user_name IN account_info.user_name%TYPE,
user_password IN account_info.user_password%TYPE)
AS
BEGIN
INSERT INTO account_info (user_name, user_password)
VALUES ('ABC', 'password123');
-- check the user exists or not, if yes proceed
EXECUTE IMMEDIATE
'CREATE USER ' || user_name || ' IDENTIFIED BY ' || user_password;
-- do the rest of the activities such as grant roles, privilges etc.
END;
/

Deleting a non-unique procedure on DB2

In my attempts to edit a procedure using the line
CREATE OR DROP PROCEDURE
I have created two procedures with the same name, how can I delete them?
The error I receive whenever I attempt to drop it is
Reference to Rountine BT_CU_ODOMETER was made without a signature, but the routine is not unique in its schema.
SQLSTATE = 42725
I am using DB2
Assuming this is DB2 for LUW.
DB2 allows you to "overload" procedures with the same name but different number of parameters. Each procedure receives a specific name, which can be provided by you or generated by the system and which will be unique.
To determine the specific names of your procedures, run
SELECT ROUTINESCHEMA, ROUTINENAME, SPECIFICNAME FROM SYSCAT.ROUTINES
WHERE ROUTINENAME = 'BT_CU_ODOMETER'
You can then drop each procedure individually:
DROP SPECIFIC PROCEDURE <specific name>
In case you need to drop all the overloads of a given procedure name, here's a handy script based on mustaccio's answer
BEGIN
FOR rec AS
SELECT SPECIFICNAME, ROUTINETYPE
FROM SYSCAT.ROUTINES
WHERE ROUTINENAME = 'ROUTINE_NAME'
DO
IF rec.ROUTINETYPE = 'P' THEN
EXECUTE IMMEDIATE 'DROP SPECIFIC PROCEDURE ' || rec.SPECIFICNAME;
ELSE
EXECUTE IMMEDIATE 'DROP SPECIFIC FUNCTION ' || rec.SPECIFICNAME;
END IF;
END FOR;
END
PROBLEM
When multiple stored procedures are created with the same name but with a different number of parameters, then the stored procedure is considered overloaded. When attempting to drop an overloaded stored procedure using the DROP PROCEDURE statement, the following error could result:
db2 drop procedure SCHEMA.PROCEDURENAME
DB21034E The command was processed as an SQL statement because it was not valid Command Line Processor command. During SQL processing it returned: SQL0476N Reference to routine "SCHEMA.PROCEDURENAME" was made without a signature, but the routine is not unique in its schema. SQLSTATE=42725
CAUSE
The error is returned because the stored procedure is overloaded and therefore the procedure is not unique in that schema. To drop the procedure you must specify the data types that were specified on the CREATE PROCEDURE statement or use the stored procedure's specific name per the examples below.
SOLUTION
In order to drop an overloaded stored procedure you can use either of the following statements:
db2 "DROP PROCEDURE procedure-name(int, varchar(12))"
db2 "DROP SPECIFIC PROCEDURE specific-name"
Note: The specific-name can be identified by selecting the SPECIFICNAME column from syscat.routines catalog view.
A procedure can be dropped like:
DROP PROCEDURE INORUP RESTRICT;
The parameter RESTRICT is required. It avoids dropping a procedure used by a trigger. The procedure package is dropped.
Packages and plans calling the procedure are invalidated.

Dynamic SQL not working as expected

create or replace procedure createtables
Authid current_user as
begin
execute immediate 'create table newcustomer as select * from customer';
end;
create or replace procedure e
is
begin
createtables;
select * from newcustomer;
end;
I got two procedures above. first one will create a new tables called newcustomer, second procedure will call the first procedure and query to the newcustomer table. when I try to compile this code, it says the table is not yet created, I don't really get it as I have called createtables procedure so I assume I have created the table.
Any help will be appreciated. Thanks
Compiling the second procedure without executing the first procedure first will fail, since the table has not been created.
You cannot compile a procedure that relies on objects that do not exist.
Use EXEC createtables before creating procedure e, and do not call createtables in there.
Procedure e will also not compile because you are not using the results of select * from newcustomer as cursor or store the results into variables.
EDIT:
Instead of procedures, you could use an anonymous block. Put the following into a file and execute it (via SQL*Plus for example):
Create Table newcustomer As Select * From customer;
Begin
Null; --# Do something with your new table in here.
End;
/