ORA-22285: non-existent directory or file for FILEOPEN operation - plsql

I am trying to insert code of XML file into a column which is of XMLtype
I have below query which is working fine
select extract(xmlcol,'/*')
from (SELECT xmltype(BFILENAME('MEDIA_DIR', 'xmldata1.xml'),nls_charset_id('UTF-8')) xmlcol FROM dual)
On running this query I am getting the Xmlcode inside the xml file(xmldata1) in one row.
Now, As per my requirement I have created a type which reads data from the XMLfile.
Below is the code of member function of the type:
create or replace type body t_emp
as
member function get_xml return xmltype is
v_xml xmltype;
begin
select extract(xmlcol,'/*') into v_xml
from (SELECT xmltype(BFILENAME('MEDIA_DIR', 'xmldata1.xml'),nls_charset_id('UTF-8')) xmlcol FROM dual);
return v_xml;
end get_xml;
end;
I am calling this type member function in my code. Below is the code
declare
t_emp1 t_emp;
r1 xmltype;
begin
t_emp1 := t_emp(2);
r1 := t_emp1.get_xml;
insert into EMPLOYEE_XML values (t_emp1.id,r1);
end;
Here variable r1 is of importance as I am fetching XML data in this variable.
On running this code I am getting below error:
ORA-22285: non-existent directory or file for FILEOPEN operation
I am not able to understand why this error is coming as the directory exist. I am able to run previously mentioned SQL query.
Thanks!!

The user you are creating the type as only has permissions on the directory object granted through a role. Privileges from roles are not inherited by stored PL/SQL blocks by default. When you run the SQL directly the role is enabled; when you run in through the PL/SQL member function the role is disabled and the directory is not visible to your user. You need the directory privileges to be granted directly to your user.
In your case you are working in the SYSTEM schema (so the directory privilege is coming via the EXP_FULL_DATABASE role, which comes from DBA), which is a bad idea; it is not good practice to create objects in that schema (or any built-in schema), so granting privileges directly to `SYSTEM would also be a mistake here.
You should create a new user/schema, with the minimum privileges it needs. You've already created the directory object, so the new user set-up would include:
grant read,write on media_dir to your_new_user;
You can then create the type and its member function in that new schema, and you'll be able to execute it from your anonymous block.
If you can only have privileges granted through a role then you could instead change the type declaration to use invoker's rights
create or replace type t_emp authid current_user as object ...
That would even work in your current scenario, sticking with SYSTEM; but again you really shouldn't be working in that schema.

Related

Shared database and "An owner referenced by user does not have EXECUTE PROCEDURE access to" procedure

Is there a way around it in Teradata, if you have rights to create a procedure in the shared database, but fail to execute the first procedure inside the second procedure you created in the same database.
Attempted with SQL INVOKER and SQL CREATOR.
Tried an option to grant the execution explicitly, ended with the:
The user doesn't have CREATE PROCEDURE WITH GRANT OPTION access to database
What else can be done here?
.login server1/$user1,$pwd1
database shared_db;
replace proc proc1
SQL SECURITY CREATOR
begin
end;
replace proc proc2
begin
call proc1;
end;
-- errors
EDIT for Fred:
Database holding the procedures is shared (aka shared_db). User1 (who has the rights to create procedures in the shared_db) is creating proc1 in it, and proc2; the latter executes proc1 from its body.
EDIT 2 - concluded, setup needed was indeed (which is not that obvious)
GRANT EXECUTE ON shared_db.proc1 to shared_db;
The creator of PROC1 can
GRANT EXECUTE PROCEDURE ON shared_db.proc1 TO shared_db;

How to parameterise a SQL REVOKE command

How can I parameterise a SQL REVOKE command?
DECLARE #ViewName nvarchar = 'MyViewName'
DECLARE #UserRole nvarchar = 'MyRoleName'
REVOKE SELECT ON [#ViewName] TO [#UserRole]
Outputs the following error:
Cannot find the object '#ViewName', because it does not exist or you do not have permission.`
Do I need to use Dynamic SQL to solve this or is there another way?
My actual Use Case is in ASP.NET SqlDataClient and the code is being generated, so I have limited control over it. The code being sent to SQL (sniffed by SQL Profiler) is:
exec sp_executesql N'REVOKE SELECT ON [#ViewName] TO [#UserRole]',N'#ViewName nvarchar(24),#UserRole nvarchar(12)',#ViewName=N'MyViewName',#UserRole=N'MyUserRole'
Because GRANT and REVOKE require considerable permissions themselves, it's usually attractive to do this in a stored procedure that builds the dynamic SQL and can execute the commands under a different credential. That way you can selectively assign the right to REVOKE by controlling access to the stored procedure, add auditing if this ever becomes a requirement, and last but not least you can keep using parameters.
In this case:
CREATE PROCEDURE dbo.RevokeSelect(#ObjectName NVARCHAR(128), #RoleName NVARCHAR(128))
WITH EXECUTE AS OWNER AS BEGIN
SET NOCOUNT ON;
DECLARE #SQL NVARCHAR(MAX);
SELECT #SQL = REPLACE(REPLACE(
N'REVOKE SELECT ON #ObjectName TO #RoleName;',
'#ObjectName', QUOTENAME(#ObjectName)),
'#RoleName', QUOTENAME(#RoleName))
;
-- For debugging
--PRINT #SQL
EXEC (#SQL)
END;
GO
GRANT EXECUTE ON dbo.RevokeSelect TO [application_login];
Thanks to EXECUTE AS OWNER, the [application_login] account needs no additional permissions; it can REVOKE SELECT on any object in the database through the stored procedure. This can be exactly what you want, but if it's not, you should remove EXECUTE AS OWNER and grant individual CONTROL permission on the objects (but this, of course, allows lots of other operations as well).
Take care that a procedure that performs dynamic SQL needs careful review to ensure it's not susceptible to SQL injection, just like anything else that uses dynamic SQL. Obviously this is even more important if the procedure uses EXECUTE AS OWNER, since it could do anything. In this case, applying QUOTENAME to both parameters takes care of that.
Last but not least, while EXECUTE AS OWNER is simple and convenient, it will fail if the database owner is not an account but a group. In this case, if you want to delegate permissions, you'll have to create a proxy account for use in EXECUTE AS or sign the stored procedure with a certificate. You may want to do this anyway if developers can't be trusted with the power of EXECUTE AS OWNER. That goes beyond the scope of this answer, but Erland Sommerskog has an excellent writeup on this topic.

SP Not Found When It Clearly Exists

I copied a database from a live MSSQL server to my local one, and was able to log in correctly. I am having a problem however in that when it is time to call a stored procedure the Asp.Net application keeps telling me the SP does not exist, when it clearly does.
I am using windows authentication but on the server I was using credentials, could this be the problem?
Also, all of the SP's have my online username attached to their name, as in username.StoredProcedurenName.
Please help I have been trying to fix this for hours.
I just noticed that when I attempt to run the SP from the SQL Management Studio it works, but it appends the username to the SP such as:
USE [DBNAME]
GO
DECLARE #return_value int
EXEC #return_value = [username].[SPNAME]
SELECT 'Return Value' = #return_value
GO
If I remove the username, it says the same thing (SP not found). How do I get around this?
I suspect you are calling your stored procedure without specifying the schema. When calling a stored procedure (or accessing a table, view, etc) that's not in the default schema that your account is configured for, usually dbo, you need to explicitly include the schema like the sql command below
SqlCommand cmd = new SqlCommand("username.StoredProcedurenName", mySqlConnection);
It's likely what Jason said. The solution has to do with rights and ownership. When you see the SP in the SQL Management Studio, under Programmability->Stored Procedures, your SP should have a prefix like "dbo." or "GateKeeper."
If the SP has "dbo." as the prefix, the user account with which you're connecting to the DB just be part of the database owners (dbo) group, otherwise you won't have access to it. So, you can either add the user to that group, or create the stored procedure ("create procedure spBlahBlah as ..") using the account to plan to run the program under; when you call it you use "exec GateKeeper.spBlahBlah" to stipulate the Schema.StoredProcedureName.
Those are your two choices.

Reference different schema using "alter session set current_schema" inside a package

Is it possible to do
alter session set current_schema=MySchema;
inside a package?
Our asp.net web application call Oracle packages. We'd like to connect to database with an Oracle user that is not the owner of MySchema. For that, we grant execute permissions on Other_User to package MyPackage.
Example:
grant execute on MySchema.MyPackage to Other_User
But when web app connects to Oracle and try to execute the stored procedures of MyPackage, it gets errors because tables don't belong to Other_User.
One way to avoid errors is creating synonyms, but we would prefere to use
alter session set current_schema=MySchema;
if possible, inside the package.
EDIT: When trying to put "alter session" in package:
You cannot use DDL statements (which ALTER SESSION is) directly in PL/SQL.
You need to use an EXECUTE IMMEDIATE:
execute immediate 'alter session set current_schema=MySchema';

Adding tempdb items on startup in SQL Server

How could I add some items to the tempdb anytime SQL Server starts up?
I'm no expert at this, but our ASP SessionState is stored in the DB and for some reason the tempdb items used for the session state get dropped anytime the server restarts. Not only do I need to recreate the items, but I also have to recreate the User mappings to tempdb. I have a script that does it, but I can't figure out how to run it on SQL startup
-- Use TempDB
use tempdb
go
-- Create Temp tables if they don't exist
IF NOT EXISTS(
SELECT 1 FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE='BASE TABLE'
AND TABLE_NAME = 'ASPStateTempSessions')
BEGIN
EXECUTE [ASPState].[dbo].[CreateTempTables]
END
-- If ASPSessionState user isn't mapped to temp db, map it
IF IS_MEMBER('ASPSessionState') IS NULL
create user ASPSessionState from login ASPSessionState
-- Give ASPSessionState user read/write permissions to tempdb
exec sp_addrolemember db_datareader, ASPSessionState
go
exec sp_addrolemember db_datawriter , ASPSessionState
go
Um, if you've used the standard settings to enable ASP.Net session state in tempdb, the system should have generated a stored proc (ASPState_Startup) as follows in the master database. This stored proc is configured to run automatically on SQL Server startup:
USE master
GO
DECLARE #sstype nvarchar(128)
SET #sstype = N'sstype_temp'
IF UPPER(#sstype) = 'SSTYPE_TEMP' BEGIN
DECLARE #cmd nchar(4000)
SET #cmd = N'
/* Create the startup procedure */
CREATE PROCEDURE dbo.ASPState_Startup
AS
EXECUTE ASPState.dbo.CreateTempTables
RETURN 0'
EXEC(#cmd)
EXECUTE sp_procoption #ProcName='dbo.ASPState_Startup', #OptionName='startup', #OptionValue='true'
END
So, the temp tables should be being recreated anyway, unless something has been altered since installing.
If additional permissions are required, I'd look to extending the existing CreateTempTables procedure in ASPState.
If this isn't working correctly, you might try using the aspnet_regsql command (found under %Windir%\Microsoft.Net\Framework\<framework version - to remove then re-add session state support to the server. You'd want to use -ssremove then -ssadd, but I'd suggest passing /? first to see all of the applicable options.

Resources