I have a successfully compiled procedure under SYSTEM schema.
create or replace procedure get_file_list as
ns varchar2(1024);
cursor c_my_directory is
select directory_name, directory_path from all_directories where directory_path like '/home/oracle/EDI%';
begin
-- before generating the file list, the temporary table is deleted
delete from edi.temp_EDI_file_list;
for each_directory in c_my_directory loop
-- it reads the contents of my_directory into a table called X$FRBMSFT
sys.dbms_backup_restore.searchfiles (each_directory.directory_path, ns);
for each_file in (select fname_krbmsft as name from X$KRBMSFT) loop
insert into edi.temp_edi_file_list
values (each_directory.directory_name, each_file.name);
end loop;
end loop;
commit;
exception
when others then
raise_application_error (-20001,sqlcode || ' ' || sqlerrm);
end get_file_list;
[.. it was created under SYSTEM schema because I am not allowed to grant select on X$FRBMSFT to user "edi"].
I granted execute privilegies to user "edi" on this procedure.
[.. connected as SYSTEM, role SYSDBA, I executed grant execute on system.get_file_list to EDI;]
When I am trying to execute the procedure (execute system.get_file_list;) with user "edi" it return the error
PLS-00905: object SYSTEM.GET_FILE_LIST is invalid
Can someone, please, give me a hint about what am I doing wrong?
Thank you,
In the end I managed to create the procedure, with some help from the link provided by #APC.
... conected as SYSTEM
create or replace view file_list as select fname_krbmsft from X$KRBMSFT readonly;
create or replace procedure searchfiles (pattern in out nocopy varchar2, ns in out nocopy varchar2) authid definer as
begin
dbms_backup_restore.searchfiles(pattern, ns);
end searchfiles;
GRANT SELECT ON FILE_LIST TO EDI;
GRANT EXECUTE ON SEARCHFILES TO EDI;
... conected as EDI
create or replace procedure get_file_list as
ns varchar2(1024);
cursor c_my_directory is
select directory_name, directory_path from all_directories where directory_path like '/home/oracle/EDI%';
begin
-- before generating the file list, the temporary table is deleted
delete from edi.temp_EDI_file_list;
for each_directory in c_my_directory loop
-- it reads the contents of all directories into a table called X$FRBMSFT via procedure SEARCHFILES
sys.SEARCHFILES (each_directory.directory_path, ns);
-- it interogate the X$FRBMSFT via file_list view
for each_file in (select fname_krbmsft as name from sys.file_list) loop
insert into temp_edi_file_list
values (each_directory.directory_name, each_file.name);
end loop;
end loop;
commit;
exception
when others then
raise_application_error (-20001,sqlcode || ' ' || sqlerrm);
end get_file_list;
The difference was made by the way they were called the objects created with user SYSTEM. They were called with SYS.xxx instead of SYSTEM.xxx
Related
When I call the function, it works in SQL*Plus but doesn't work in PowerBI.
I opened PowerBI> Get Data> Oracle> Entered server name> Went to advanced options to enter sql below
SELECT * FROM TABLE(TESTPOWERBI);
Error: We encountered an error while trying to connect. SQL command not properly ended.
Anyone have experience in solving this?
DROP TYPE VW_PEOPLE_TABLE;
DROP TYPE VW_PEOPLE_TYPE;
CREATE OR REPLACE TYPE VW_PEOPLE_TYPE AS OBJECT(NAME VARCHAR2(70), ALIAS VARCHAR2(90));
/
CREATE OR REPLACE TYPE VW_PEOPLE_TABLE AS TABLE OF VW_PEOPLE_TYPEL
/
CREATE OR REPLACE FUNCTION TESTPOWERBI RETURN VW_PEOPLE_TABLE
PIPELINED
AUTHID CURRENT_USER
AS
VWT VW_PEOPLE_TABLE;
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
SELECT
VW_PEOPLE_TYPE(NAME, ALIAS)
BULK COLLECT
INTO VWT
FROM MYDATABASE;
FOR i in 1 .. VWT.COUNT
LOOP
PIPE ROW (VW_PEOPLE_TYPE(VWT(i).NAME, VWT(i).ALIAS));
END LOOP;
END TESTPOWERBI;
/
GRANT EXECUTE ON TESTPOWERBI TO PUBLIC;
You mixed your delimiters ; and /.
Clean it up and add one at the end behind GRANT EXECUTE ON TESTPOWERBI TO PUBLIC.
In addition you got a typo: for i in 1 .. vwt.count only two dots.
This should work:
DROP TYPE VW_PEOPLE_TABLE;
DROP TYPE VW_PEOPLE_TYPE;
CREATE OR REPLACE TYPE VW_PEOPLE_TYPE AS OBJECT
(
NAME VARCHAR2 (70),
ALIAS VARCHAR2 (90)
);
CREATE OR REPLACE TYPE VW_PEOPLE_TABLE AS TABLE OF VW_PEOPLE_TYPEL;
CREATE OR REPLACE FUNCTION TESTPOWERBI
RETURN VW_PEOPLE_TABLE
PIPELINED
AUTHID CURRENT_USER
AS
VWT VW_PEOPLE_TABLE;
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
SELECT VW_PEOPLE_TYPE (NAME, ALIAS)
BULK COLLECT INTO VWT
FROM MYDATABASE;
FOR i IN 1 .. VWT.COUNT
LOOP
PIPE ROW (VW_PEOPLE_TYPE (VWT (i).NAME, VWT (i).ALIAS));
END LOOP;
END TESTPOWERBI;
/
GRANT EXECUTE ON TESTPOWERBI TO PUBLIC;
Here some discussion about delimiters:
When do I need to use a semicolon vs a slash in Oracle SQL?
Here is the definition of the stored procedure:
CREATE OR REPLACE PROCEDURE usp_dropTable(schema VARCHAR, tblToDrop VARCHAR) IS
BEGIN
DECLARE v_cnt NUMBER;
BEGIN
SELECT COUNT(*)
INTO v_cnt
FROM all_tables
WHERE owner = schema
AND table_name = tblToDrop;
IF v_cnt > 0 THEN
EXECUTE IMMEDIATE('DROP TABLE someschema.some_table PURGE');
END IF;
END;
END;
Here is the call:
CALL usp_dropTable('SOMESCHEMA', 'SOME_TABLE');
For some reason, I keep getting insufficient privileges error for the EXECUTE IMMEDIATE command. I looked online and found out that the insufficient privileges error usually means the oracle user account does not have privileges for the command used in the query that is passes, which in this case is DROP. However, I have drop privileges. I am really confused and I can't seem to find a solution that works for me.
Thanks to you in advance.
SOLUTION:
As Steve mentioned below, Oracle security model is weird in that it needs to know explicitly somewhere in the procedure what kind of privileges to use. The way to let Oracle know that is to use AUTHID keyword in the CREATE OR REPLACE statement. If you want the same level of privileges as the creator of the procedure, you use AUTHID DEFINER. If you want Oracle to use the privileges of the user currently running the stored procedure, you want to use AUTHID CURRENT_USER. The procedure declaration looks as follows:
CREATE OR REPLACE PROCEDURE usp_dropTable(schema VARCHAR, tblToDrop VARCHAR)
AUTHID CURRENT_USER IS
BEGIN
DECLARE v_cnt NUMBER;
BEGIN
SELECT COUNT(*)
INTO v_cnt
FROM all_tables
WHERE owner = schema
AND table_name = tblToDrop;
IF v_cnt > 0 THEN
EXECUTE IMMEDIATE('DROP TABLE someschema.some_table PURGE');
END IF;
END;
END;
Thank you everyone for responding. This was definitely very annoying problem to get to the solution.
Oracle's security model is such that when executing dynamic SQL using Execute Immediate (inside the context of a PL/SQL block or procedure), the user does not have privileges to objects or commands that are granted via role membership. Your user likely has "DBA" role or something similar. You must explicitly grant "drop table" permissions to this user. The same would apply if you were trying to select from tables in another schema (such as sys or system) - you would need to grant explicit SELECT privileges on that table to this user.
You should use this example with AUTHID CURRENT_USER :
CREATE OR REPLACE PROCEDURE Create_sequence_for_tab (VAR_TAB_NAME IN VARCHAR2)
AUTHID CURRENT_USER
IS
SEQ_NAME VARCHAR2 (100);
FINAL_QUERY VARCHAR2 (100);
COUNT_NUMBER NUMBER := 0;
cur_id NUMBER;
BEGIN
SEQ_NAME := 'SEQ_' || VAR_TAB_NAME;
SELECT COUNT (*)
INTO COUNT_NUMBER
FROM USER_SEQUENCES
WHERE SEQUENCE_NAME = SEQ_NAME;
DBMS_OUTPUT.PUT_LINE (SEQ_NAME || '>' || COUNT_NUMBER);
IF COUNT_NUMBER = 0
THEN
--DBMS_OUTPUT.PUT_LINE('DROP SEQUENCE ' || SEQ_NAME);
-- EXECUTE IMMEDIATE 'DROP SEQUENCE ' || SEQ_NAME;
-- ELSE
SELECT 'CREATE SEQUENCE COMPTABILITE.' || SEQ_NAME || ' START WITH ' || ROUND (DBMS_RANDOM.VALUE (100000000000, 999999999999), 0) || ' INCREMENT BY 1'
INTO FINAL_QUERY
FROM DUAL;
DBMS_OUTPUT.PUT_LINE (FINAL_QUERY);
cur_id := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.parse (cur_id, FINAL_QUERY, DBMS_SQL.v7);
DBMS_SQL.CLOSE_CURSOR (cur_id);
-- EXECUTE IMMEDIATE FINAL_QUERY;
END IF;
COMMIT;
END;
/
you could use "AUTHID CURRENT_USER" in body of your procedure definition for your requirements.
Alternatively you can grant the user DROP_ANY_TABLE privilege if need be and the procedure will run as is without the need for any alteration. Dangerous maybe but depends what you're doing :)
I have a stored procedure proc1 without parameters. I want to extract data from this stored procedure. How can I get that? Could you help me?
Stored procedure:
create procedure proc1
as
begin
select e_id, e_nm, e_sal
from emp
where e_id like 'e%';
end proc1;
You can do this in Oracle 12.1 or above:
create or replace procedure demo
as
rc sys_refcursor;
begin
open rc for select * from dual;
dbms_sql.return_result(rc);
end demo;
This requires an Oracle 12.1 or later client/driver to handle the implicit result set.
For more details, see Implicit Result Sets in the Oracle 12.1 New Features Guide, Tom Kyte's Blog, Oracle Base etc.
Here's one possible solution:
Declaration:
create procedure proc1 (emp_row IN OUT emp%rowtype)
as
begin
select * --e_id, e_nm, e_sal
into emp_row
from emp
where e_id like 'e%';
end proc1;
Use case:
DECLARE
l_emp_row emp%rowtype;
BEGIN
proc1(l_emp_row);
-- Here you can access every column of table "emp", like so:
-- dbms_output.put_line('e_id: ' || to_char(l_emp_row.e_id));
-- dbms_output.put_line('e_nm: ' || to_char(l_emp_row.e_nm));
-- dbms_output.put_line('e_sal: ' || to_char(l_emp_row.e_sal));
END;
Is there anything special that you need to get out of the Procedure?
Cheers
you create a view this query. (recomended)
Oracle procedure is not return any data. So, you do not see any result. Your result get buffer but not print screen. if You need a procedure, insert all data another table.
create procedure proc1
as
begin
insert into new_table select e_id, e_nm, e_sal from emp where e_id like 'e%';
end proc1;
Another way; You create a function. Because function outputs and inputs. This function;
for example Create an Oracle function that returns a table
I want to grant the CREATE ANY DIRECTORY permission to a user, with the following restriction: all directories created by this user must be inside of /foo/bar, and any attempt to create a directory outside of this should fail with a permission error. How may I do this on Oracle 11G or 12C?
That depends, if you want to restrict which OS directories Oracle can access from utl_file commands, you can set the utl_file_dir parameter. Unfortunately, this parameter is system wide, so you won't be able to grant/revoke for a specific user using this parameter. Also keep in mind that if you make changes to this parameter, those changes won't go into effect until the Oracle database is restarted:
alter system set utl_file_dir = '/foo/bar' scope=spfile;
shutdown immediate;
startup open;
Consult the 12.1 Oracle Docs for more information regarding utl_file_dir.
That said, if you really want to restrict who can create Oracle Directories to specific OS directories, a procedure would be appropriate for that task since that would allow you to have finer grained control (and limit who has the very powerful create any directory privilege to the owner of the procedure):
sqlplus kjohnston
create or replace procedure mydircreate (p_dir varchar2)
as
ex_custom EXCEPTION;
PRAGMA EXCEPTION_INIT( ex_custom, -20001 );
begin
if lower(p_dir) not like '/foo/bar/%' then
raise_application_error( -20001, 'Not authorized' );
end if;
execute immediate 'create or replace directory mydir as ''' || p_dir || '''';
end mydircreate;
create user testuser identified by <password>;
grant create session to testuser;
grant execute on kjohnston.mydircreate to testuser;
exit;
sqlplus testuser
SQL> exec kjohnston.mydircreate('mydir', '/randomdir');
ORA-20001: Not authorized
SQL> exec kjohnston.mydircreate('mydir', '/foo/bar/baz');
PL/SQL procedure successfully completed.
You can include this restriction in trigger. List of system events and attributes Working with system events
CREATE OR REPLACE TRIGGER trg_before_ddl
BEFORE DDL ON DATABASE
declare
v_sql ORA_NAME_LIST_T;
v_ddl varchar2(4000);
v_cnt BINARY_INTEGER;
is_valid number;
begin
if ora_sysevent in ('CREATE') and ora_dict_obj_type = 'DIRECTORY' then
v_cnt := ora_sql_txt (v_sql);
FOR i IN 1..v_cnt LOOP
v_ddl := v_ddl || RTRIM (v_sql (i), CHR (0));
END LOOP;
v_ddl := regexp_substr(v_ddl,'AS ''(.*)''', 1, 1, 'i', 1 ); -- get path from ddl_statement
-- check valid directory here, path is in v_ddl ;
is_valid := REGEXP_instr(v_ddl,'^/valid_dir/.*$');
if (is_valid = 0) then
raise_application_error(-20000,'Directory is not valid' || v_ddl);
end if;
end if;
END;
/
CREATE DIRECTORY valid_dir AS '/valid_dir/xyz';
CREATE DIRECTORY invalid_dir AS '/invalid_dir/xyz';
I wrote a procedure in PL/SQL to delete rows from a table,However,if that record does not exist,then throws some error like this: DBMS_OUTPUT.PUT_LINE('No such record'); My procedure is:
CREATE OR REPLACE PROCEDURE del_cn2
(c_cntry_id IN COUNTRIES.COUNTRY_ID%TYPE
)
IS
v_error_code NUMBER;
BEGIN
DELETE from countries
WHERE country_id =c_cntry_id;
IF SQL%NOTFOUND THEN
DBMS_OUTPUT.PUT_LINE('No such record');
END IF;
EXCEPTION WHEN OTHERS THEN
v_error_code :=SQLCODE;
IF v_error_code =-2292 THEN
RAISE_APPLICATION_ERROR(-20004,'Organization '||TO_CHAR(c_cntry_id)||' site
details defined for it.');
END IF;
END;
/
However,when I execute this procedure and provide a record that does not exist in my table,it gives message "Procedure completed successfully" I am using this to execute:
Execute procedure del_cn2('JJ');
Can someone please suggest?
If you want an exception to be thrown when a value that does not exist in the table is passed in, you would need to actually throw an exception. You shouldn't use dbms_output for any sort of error output. That is a very simplistic debugging tool-- you shouldn't assume that the caller will ever be able to see that output.
My guess is that you want something like
CREATE OR REPLACE PROCEDURE del_cn2
(c_cntry_id IN COUNTRIES.COUNTRY_ID%TYPE
)
IS
BEGIN
DELETE from countries
WHERE country_id =c_cntry_id;
IF SQL%ROWCOUNT = 0
THEN
raise_application_error( -20001, c_cntry_id || ' no such value.' );
END IF;
END;
try to set serverout to ON
example:
create table tst_delete (col1 int);
create procedure p_test_delete as
BEGIN
DELETE FROM tst_delete
WHERE col1 = 1;
IF (SQL%NOTFOUND)
THEN
dbms_output.put_line('No records found');
END IF;
END;
then call the procedure in SqlPlus
SQL> exec p_test_delete;
PL/SQL procedure successfully completed
same issue that you described - no insformation...
next try with output activated
SQL> set serverout on
SQL> exec p_test_delete;
No records found
PL/SQL procedure successfully completed
SQL>