Create user through procedure on another database via database link - database

I'd like to create users through procedure on another database via database link.I am getting error while executing procedure.
Here are the code which i used.
create or REPLACE PROCEDURE hostname
(host_name in varchar2,user_name in VARCHAR2, pass_word in VARCHAR2,
table_space in varchar2,pro_file in varchar2)
as
db_link_name varchar2(30);
begin
select db_link into db_link_name from all_db_links where host=host_name;
EXECUTE IMMEDIATE 'dbms_utility.exec_ddl_statement#'||db_link_name||('CREATE USER '||user_name||' IDENTIFIED BY '||pass_word||'
DEFAULT TABLESPACE '||table_space||' PROFILE '|| pro_file||' ACCOUNT UNLOCK');
EXECUTE IMMEDIATE 'dbms_utility.exec_ddl_statement#'||db_link_name||
('GRANT CONNECT,RESOURCE,EXECUTE_CATALOG_ROLE,Create table,create session,create view,create sequence,create procedure,create job,create synonym TO '||user_name);
end;
/
execute hostname('orcl1','rahul1','rahul','users','default');
Error: ORA-00900: invalid SQL statement ORA-06512: at
"SYS.HOSTNAME", line 6 ORA-06512: at line 1
00900. 00000 - "invalid SQL statement"
*Cause:
*Action:

I think you may have got a little confused by some of the advice provided by the commenter mustaccio. What he meant to say is that the SQL strings in your EXECUTE IMMEDIATE statements need to use PL/SQL blocks in order to call stored procedures.
In other words, instead of writing
EXECUTE IMMEDIATE 'dbms_utility.exec_ddl_statement ... ';
you should write
EXECUTE IMMEDIATE 'BEGIN dbms_utility.exec_ddl_statement ... ; END;';
For your procedure, I would recommend doing introducing a local variable for the strings you are passing to EXECUTE IMMEDIATE. These can be notoriously tricky to get right, and if you've assigned them to a local variable you can easily output them using dbms_output.put_line. In fact, I did exactly this while fixing up the problems in your procedure. I would recommend continuing to do this if you wish to modify or extend your procedure.
Anyway, here's what I ended up with, which appeared to work:
create or REPLACE PROCEDURE hostname
(host_name in varchar2,user_name in VARCHAR2, pass_word in VARCHAR2,
table_space in varchar2,pro_file in varchar2)
as
db_link_name varchar2(30);
l_ddl_sql varchar2(4000);
begin
select db_link into db_link_name from all_db_links where host=host_name;
l_ddl_sql := 'begin dbms_utility.exec_ddl_statement#'||db_link_name||'(''CREATE USER '||user_name||' IDENTIFIED BY '||pass_word||'
DEFAULT TABLESPACE '||table_space||' PROFILE '|| pro_file||' ACCOUNT UNLOCK''); END;';
EXECUTE IMMEDIATE l_ddl_sql;
l_ddl_sql := 'begin dbms_utility.exec_ddl_statement#'||db_link_name||
'(''GRANT CONNECT,RESOURCE,EXECUTE_CATALOG_ROLE,Create table,create session,create view,create sequence,create procedure,create job,create synonym TO '||user_name || '''); END;';
EXECUTE IMMEDIATE l_ddl_sql;
end;
/

Related

PLS-00201: identifier 'DBMS_AWM' must be declared on owbsys user

After upgrading oracle database from 11gR1 to 11gR2, owbsys user's procedure get the following compilation error.
PROCEDURE OWBSYS.WB_OLAP_LOAD_CUBE
On line: 1
PLS-00201: identifier 'DBMS_AWM' must be declared
Procedure Code:
CREATE OR REPLACE PROCEDURE OWBSYS.WB_OLAP_LOAD_CUBE(olap_aw_owner VARCHAR2, olap_aw_name VARCHAR2, olap_cube_owner VARCHAR2, olap_cube_name VARCHAR2, olap_tgt_cube_name VARCHAR2) AS v varchar2(32);
BEGIN
BEGIN
DBMS_AWM.CREATE_AWCUBELOAD_SPEC(olap_cube_name, olap_cube_owner, olap_cube_name, 'LOAD_DATA');
EXCEPTION WHEN OTHERS THEN NULL;
END;
DBMS_AWM.REFRESH_AWCUBE(olap_aw_owner, olap_aw_name, olap_tgt_cube_name, olap_cube_name);
DBMS_AW.EXECUTE('upd '||olap_aw_owner||'.'||olap_aw_name ||'; commit');
BEGIN
SELECT null into v from all_olap2_aw_cube_agg_specs where aw_owner=olap_aw_owner and aw_name=olap_aw_name and aw_cube_name=olap_tgt_cube_name and aw_aggspec_name=olap_cube_name;
EXCEPTION
WHEN OTHERS THEN RETURN;
END;
DBMS_AWM.AGGREGATE_AWCUBE(olap_aw_owner, olap_aw_name, olap_tgt_cube_name, olap_cube_name);
EXCEPTION
WHEN OTHERS THEN RAISE;
END;
/
The following is the workaround to fix the issue
Step 1: Login to database as SYSDBA
Step 2: Run the following grant command
GRANT EXECUTE ON DBMS_AWM TO OWBSYS;
Step 3: Re-compile your procedure.
Hopefully, after performing the above actions, your procedure will compile without errors.

Unable to Disable a simple trigger within a stored procedure

I have a simple trigger Trig_SP_History_Delete in my database which I want to disable with a call from within a stored procedure - like this:
-- Stored procedure for SP_Testing
Use DW_dataBase
GO
-----------------------------
-- Create Simple stored procedure
ALTER PROCEDURE stage.SP_Testing
AS
BEGIN
EXEC('USE [DW_dataBase];DISABLE TRIGGER Trig_SP_History_Delete ON DW_dataBase;');
END;
-----------------------------
-- Execute stored procedure
EXEC stage.SP_Testing;
I get an error:
Cannot find the object "DW_dataBase" because it does not exist or you do not have permissions.
I have that database, and all permissions, still I get this error. Why is it so?

Schema name resolution for a stored procedure

According to books online (https://technet.microsoft.com/en-us/library/ms189915(v=sql.105).aspx) we have:
In SQL Server, if the current schema contains a procedure with the specified name, that procedure is returned. If a nonqualified stored procedure is specified, the Database Engine searches for the procedure in the following order:
• The sys schema of the current database.
• The caller's default schema if executed in a batch or in dynamic SQL; or, if the non-qualified procedure name appears inside the body of another procedure definition, the schema containing this other procedure is searched next.
• The dbo schema in the current database.
I tried testing the case with a stored procedure Proc1 which calls proc2. I define them in the same schema, but call proc2 without the schema name. It doesn't work, so what does the second part of item two on the list above mean?
Use AdventureWorks
GO
CREATE SCHEMA MySchema
GO
CREATE PROCEDURE MySchema.PROC2
AS
BEGIN
SELECT 1
END
GO
CREATE PROCEDURE MySchema.PROC1
AS
BEGIN
SELECT 2
-- calling proc2 without schema name
-- expecting it will work, since proc1 and proc2 are in same schema
EXEC PROC2
END
GO
--calling proc1 (my default schema is dbo)
--Could not find stored procedure 'PROC2'.
EXEC MySchema.PROC1
I know best practice is to always use the schema name - I'm just curious what they mean by the second item. I've tested this on version 2016.
The page you have linked to is the documentation for a specific system stored procedure. Its meaning is limited to what this stored procedure does.
CREATE PROCEDURE MySchema.PROC1
AS
BEGIN
SELECT 2
-- calling proc2 without schema name
-- expecting it will work, since proc1 and proc2 are in same schema
EXEC sp_stored_procedures 'PROC2'
END
Produces 2 result sets - the first contains 2, the second contains information about the MySchema.PROC2 stored procedure.

Oracle dynamic database link from variables

i have 3 oracle databases: db1, db2, db3.
I have created database links from db1 to db2 and db3, called db002link and db003link.
Now i have a procedure which takes as input a date and takes different actions on tables according to that input. One of them though requires to connect to one of the db2 or db3 databases. Before the execution of the procedure, i don't not know to which one, as it depends on the data gathered by the procedure itself in previous steps.
So i need to concatenate some variables to create the db link and then connect through it.
i have the variable v_dbnumber which is varchar(3) and looks like '003' for instance and is the result of a select from a table. I have tried the following:
v_dbconn := 'db'||v_dbnumber||'link'
But then the next step, select * from s1.t1#v_dbconn gets a compilation error for the procedure: ORA-04052, ORA-00604, ORA-02019 referring to the non existing connection. But the object is shown as:
#v_dbconn instead of #db003link.
Can somebody please help me with this?
If you need the statement to be dynamic, you'd need to use dynamic SQL.
If you just want to open a cursor using a dynamically generated SQL statement, you can do something like
DECLARE
l_sql_stmt varchar2(1000);
l_dblink varchar2(100) := 'db002link';
l_rc sys_refcursor;
BEGIN
l_sql := 'select * from s1.t1#' || l_dblink;
open l_rc for l_sql;
END;
Normally, though, you're doing something with the data that you're selecting. That would generally involve using either dbms_sql or EXECUTE IMMEDIATE to execute the statement and fetch data into some local variable or collection. Assuming that the table definitions are the same in each of the databases, you could do something like
EXECUTE IMMEDIATE l_sql
BULK COLLECT INTO <<some appropriate collection>>
My solution is very similar to Justin's, though I am using a procedure with dynamic sql.
APPS#tst> CREATE OR REPLACE PROCEDURE test_dblink(
2 db_link VARCHAR2 )
3 AS
4 v_sql VARCHAR2(500);
5 v_test dual.dummy%TYPE;
6 BEGIN
7 v_sql := 'select dummy from dual#'|| db_link;
8 EXECUTE IMMEDIATE v_sql INTO v_test;
9 DBMS_OUTPUT.PUT_LINE(v_test);
10 END;
11 /
Procedure created.
APPS#tst> commit;
Commit complete.
APPS#tst>
APPS#tst>
APPS#tst>
APPS#tst> begin
2 test_dblink('db003link');
3 end;
4 /
X
PL/SQL procedure successfully completed.
This does not have error handling and it assumes one record will be returned (typically not a good assumption).

Is it possible to find the database a stored procedure was called from?

Imagine I have a procedure in database A on SQL Server 2008R2+.
USE A
GO
CREATE PROCEDURE dbo.My_Test
AS
BEGIN
PRINT ORIGINAL_DB_NAME()
PRINT DB_NAME()
END
GO
Now I call that procedure while in database B.
USE B
GO
EXEC A.dbo.My_Test
It will return this:
A
A
ORIGINAL_DB_NAME() gives you the default database from your login if there is one. In this case it's A but it could be anything, it's not the most recent/current/super-context database.
Now imagine in My_Test you want to do a RESTORE DATABASE B. This works if you called it while you were in master or A, but fails with an error if you were in B (because that database is in use).
Msg 3102, Level 16, State 1, Line 2
RESTORE cannot process database 'B' because it is in use by this session. It is recommended that the master database be used when performing this operation.
I am planning to catch and handle that exception, but I found the problem interesting as there doesn't appear to be a way to "discover" that you were in B ahead of time. And you can't do a USE master in the procedure to avoid the problem either (use statements aren't valid in procedures).
Is it possible to find that you were in database B ahead of time?
1/ Send DB name as parameter to your stored proc
or
2/ To make SP run in the context of current connection you need create your SP on master database and make it a system object.
USE MASTER
GO
-- name should start with "sp_"
CREATE PROCEDURE dbo.sp_My_Test
#OriginalDB SYSNAME
AS
BEGIN
PRINT ORIGINAL_DB_NAME()
PRINT DB_NAME()
END
GO
EXEC sp_ms_marksystemobject 'sp_My_Test'
GO
USE Test2
GO
EXEC master..sp_My_Test #OriginalDB = DB_NAME

Resources