Parameters on Execute Immediate Sentence (Inside a procedure) - oracle

I'm triyin to create a ORACLE USER from my User table inside a procedure. The problem is that I don't know how to call a specific column. I've tried with Camp.user.username and that stuff.
create or replace
PROCEDURE PR_USERPASS AS
BEGIN
UPDATE CAMP.USERS
SET USERNAME = (DBMS_RANDOM.string('x',15)), PASS = DBMS_RANDOM.string('x',12);
EXECUTE IMMEDIATE 'CREATE USER ' || USERNAME || ' IDENTIFIED BY ' || PASSWORD;
EXECUTE IMMEDIATE 'Grant connect to ' || USERNAME;
END PR_USERPASS;
Is there anyway to call that references in the same procedure?
Thank you in advance.

Use a cursor to loop through the Camp.Users table and access its columns. Your code would go something like this (untested):
create or replace
PROCEDURE PR_USERPASS AS
BEGIN
UPDATE CAMP.USERS
SET USERNAME = (DBMS_RANDOM.string('u',15)), PASS = DBMS_RANDOM.string('x',12);
FOR userRow IN (SELECT Username, Pass FROM Camp.Users) LOOP
EXECUTE IMMEDIATE 'CREATE USER ' || userRow.Username || ' IDENTIFIED BY ' || userRow.Pass;
EXECUTE IMMEDIATE 'GRANT CONNECT TO ' || userRow.Username;
END LOOP;
END PR_USERPASS;
Addendum: The original answer generated USERNAME as DBMS_Random.String('x', 15), which allows digits and numbers for the username and password. This caused trouble when the username began with a digit. The answer was changed to use DBMS_Random.String('u', 15) to generate only Oracle-acceptable username values. The password seemed to be OK with the leading digit.
If usernames beginning with a digit are wanted, just surround the username with double quotes:
EXECUTE IMMEDIATE 'CREATE USER "' || userRow.Username || '" IDENTIFIED BY ' || userRow.Pass;
EXECUTE IMMEDIATE 'GRANT CONNECT TO "' || userRow.Username || '"';
That said, I'm not sure if having non-standard usernames is such a good idea.
Documentation for DBMS_Random.String can be found here.

Related

Oracle pl/sql Array

let's see if somebody can help me, I need to delete rows from different tables and I did think to do it using an array so i wrote this :
DECLARE
TYPE mytype_a IS TABLE OF VARCHAR2(32) INDEX BY BINARY_INTEGER;
mytype mytype_a;
BEGIN
mytype(mytype.count + 1) := 'MYTABLE';
FOR i IN 1 .. mytype.count
LOOP
DELETE mytype(i) WHERE valid = 'N';
END LOOP;
END;
Trying to run this piece of code using sqldeveloper I get the ORA-00933 command not properly ended, if I put directly the table name it works, what am I doing wrong?
Thank you.
Thank you very much guys, it works perfectly.
This is not the correct approach. You have to use Dynamic SQL for this -
DECLARE
type mytype_a is table of varchar2(32) index by binary_integer;
mytype mytype_a;
stmt varchar(500) := NULL;
BEGIN
mytype (mytype.count + 1) := 'MYTABLE';
for i in 1..mytype.count loop
stmt := 'DELETE FROM ' || mytype(i) || ' where valid =''N''';
EXECUTE IMMEDIATE stmt;
end loop;
END;
You would need to use dynamic SQL, concatenating the table name from the collection into the statement, inside your loop:
execute immediate 'DELETE FROM ' || mytype(i) || ' where valid = ''N''';
Or you can put the statement into a variable so you can display it for debugging purposes, and then execute that, optionally with a bind variable for the valid value:
stmt := 'DELETE FROM ' || mytype(i) || ' where valid = :valid';
dbms_output.put_line(stmt);
execute immediate stmt using 'N';
dbms_output.put_line('Deleted ' || sql%rowcount || ' row(s)');
... which I've made also display how many rows were deleted from each table. Note though that you shoudln't rely on the caller being able to see anything printed with dbms_output - it's up to the client whether it shows it.
The whole anonymous block would then be:
DECLARE
type mytype_a is table of varchar2(32) index by binary_integer;
mytype mytype_a;
stmt varchar2(4000);
BEGIN
mytype (mytype.count + 1) := 'MYTABLE';
for i in 1..mytype.count loop
stmt := 'DELETE FROM ' || mytype(i) || ' where valid = :valid';
dbms_output.put_line(stmt);
execute immediate stmt using 'N';
dbms_output.put_line('Deleted ' || sql%rowcount || ' row(s)');
end loop;
END;
/
You could use a built-in collection type to simplify it even further.
db<>fiddle showing some options.
Hopefully this doesn't apply, but if you might have any tables with quoted identifiers then you would need to add quotes in the dynamic statement, e.g.:
stmt := 'DELETE FROM "' || mytype(i) || '" where valid = :valid';
... and make sure the table name values in your collection exactly match the names as they appear in the data dictionary (user_tables.table_name).

How to test if a variable contains special characters other than numbers , letters and # and _ and $?

There is a variable representing a string :
create or replace procedure create_user(login varchar2)
is
begin
execute immediate 'create user "' || login || '" identified by 1 default tablespace tbs_sse';
execute immediate 'grant create session to "' || login || '"';
execute immediate 'grant select any sequence to "' || login || '"';
end;
How to test if the variable login contains special characters other than numbers , letters and # and _ and $ ?
Just you can check using regexp. Create your regexp based on what you are expecting.
SELECT count(1) into counter
FROM dual
WHERE NOT REGEXP_LIKE (LOGIN_VAR , '^[a-zA-Z0-9_$#]+$');
IF (counter != 0) THEN
--Invalid Login alert, error return
END IF;
For more idea about oracle object naming rules please check link
You test - probably - in an IF statement. If so, you can use something like
........
if regexp_like(login, '[^a-zA-Z0-9_#$]')
then ........
........
Since your actual purpose is to check that the name is suitable as an Oracle identifier, you're probably better off using the built-in function dbms_assert.simple_sql_name, e.g.:
dbms_assert.simple_sql_name(login);
execute immediate 'create user "' || login ...
This will raise ORA-44003 string is not a simple SQL name if login has an unsuitable value.

Grant select on a table forcing WHERE

I have a table with users data (email, name, surname, username, password..) and I want to grant each user to see only his own data (like seeing his profile). I have been trying to do it this way:
create or replace
PROCEDURE PR_OWNDATA AS
BEGIN
FOR userRow IN (SELECT COD_USUARIO, USERNAME FROM CAMP.USERS) LOOP
EXECUTE IMMEDIATE 'GRANT SELECT ON CAMP.USERS TO "' || userRow.USERNAME || '" WHERE COD_USUARIO = ' || userRow.COD_USUARIO || ';';
END LOOP;
END PR_OWNDATA;
It doesn't work (ORA 06550 "line %s, column %s:\n%s"). But I can't see where is the problem..
I have think about create a VIEW per USER in this way
CREATE VIEW userRow.USERNAME.V_DATOSALUMNO AS SELECT * FROM CAMP.USERS WHERE COD_USUARIO = ' || userRow.COD_USUARIO || ';';
But I don't know if it is the correct way..
Thank you in advance.
I don't understand the name you're giving to the view, but the code should be:
CREATE VIEW my_view_name
AS
SELECT *
FROM CAMP.USERS
WHERE COD_USUARIO = SYS_CONTEXT('USERENV', 'SESSION_USER')
Of course if you're not salting and hashing that password then you're doing it all wrong.

Regenerate all sequences in db (oracle)

Good day. I have a very specific task: regenerate all sequences in database. There is a 400+ tables in it, so I can't do it by hands.
Can somebody help me to do it?
Thanks a lot..
Please note this is highly dangerous. You may very well make mistakes. Run the select first to check what you're about to do and create a table of the select so you can re-create the synonyms manually later if you need to.
Using all_synonyms or dba_synonyms instead of user_synonyms may result in dropping system synonyms do not do this if you want your database to work afterwards
I'd also recommend testing the code on 1 test synonym you create to ensure that it does exactly what you want.
Plus I don't really see the point of doing this at all? If the synonyms are there why do you need to re-generate them? Or is this being done on another server? If so add #server_name after user_synonyms and remove the drop.
begin
for xx in ( select * from user_sequences ) loop
execute immediate 'drop sequence ' || xx.sequence_name;
execute immediate 'create sequence ' || xx.sequence_name
|| ' start with ' || xx.min_value
|| ' ends with ' || xx.max_value
|| case when xx.cycle_flag = 'N' then ' nocycle '
else ' cycle ' end
|| case when xx.cache_size = 0 then ' nocache '
else ' cache ' end || xx.cache_size
|| case when xx.order_flag = 'N' then ' noorder '
else ' order ' end
;
end loop;
end;

Delete an oracle-user with single quotes in the username

Through a faulty script I have created a user with single quotes around his username (i.e. his username is 'username', not username) on an Oracle 9i system. Now I want to remove that user. Neither "DROP USER 'username'" nor "DROP USER \'username\'" nor "DROP USER (SELECT username FROM all_users where user_id = 123)" worked. How do I get rid of that user?
create user "'bla'" identified by bla;
drop user "'bla'";
According to Oracle's Documentation...
"A quoted identifier begins and ends
with double quotation marks ("). If
you name a schema object using a
quoted identifier, then you must use
the double quotation marks whenever
you refer to that object."
So this...
DROP USER "username" CASCADE;
I know this is an old post, but for anyone stumbling across this as the result of a search on this problem - the issue appears to be that a database trigger is firing on drop user.
I've posted the solution I found for Oracle XE ( probably the same for other 10g releases)
here
Hope someone finds this useful,
Mike
Try DROP USER "'username'" or DROP USER ''username''. (Note that those last quotes are all single quotes)
I don't know Oracle off-hand, but might you try enclosing it in double quotes?
(I'll delete this answer if its wrong)
The following code might help you :
declare
sel_username varchar2(30);
r_user_id varchar2(30);
r_username varchar2(30);
user_cmd varchar2(200);
BEGIN
/*
This procedure will delete a single user_id and can be used to delete a user
with none displayable characters in the name
**Replace** the user_id in this script to that you want to delete !!
Author: Ulrich Henkenjohann - March 2010 / tested on ORACLE 10.2.0.4
*/
-- select the username for a special user_id. Ther username may contain none displayed characters
select username into sel_username from dba_users where user_id = 34;
select user_id, username into r_user_id , r_username from dba_users where username = sel_username ;
DBMS_OUTPUT.PUT_LINE('Selected user: ' || r_user_id || ' ' || r_username);
-- If a test is needed, an alter passwort command may be usefull
-- user_cmd := 'ALTER USER "' || r_username || '" IDENTIFIED BY PASSWORDX ';
-- Drop the selected user
user_cmd := 'DROP USER "' || r_username || '" CASCADE ';
DBMS_OUTPUT.PUT_LINE('Executing user_cmd: ' || user_cmd );
execute immediate user_cmd ;
END;
/
Once again with better format:
declare
sel_username varchar2(30);
r_user_id varchar2(30);
r_username varchar2(30);
user_cmd varchar2(200);
BEGIN
/*
This procedure will delete a single userid and can be used to delete a user
with none displayable characters in the name
**Replace the user_id in this script !!**
Author: Ulrich Henkenjohann - March 2010 / tested on ORACLE 10.2.0.4
*/
-- select the username for a special user_id. Ther username may contain none displayed characters
select username into sel_username from dba_users where user_id = 34;
select user_id, username into r_user_id , r_username from dba_users where username = sel_username ;
DBMS_OUTPUT.PUT_LINE('Selected user: ' || r_user_id || ' ' || r_username);
-- If a test is needed, an alter passwort command may be usefull
-- user_cmd := 'ALTER USER "' || r_username || '" IDENTIFIED BY PASSWORDX ';
-- Drop the selected user
user_cmd := 'DROP USER "' || r_username || '" CASCADE ';
DBMS_OUTPUT.PUT_LINE('Executing user_cmd: ' || user_cmd );
execute immediate user_cmd ;
END;
/

Resources