I'm trying to write a table trigger which queries another table that is outside the schema where the trigger will reside. Is this possible? It seems like I have no problem querying tables in my schema but I get:
Error: ORA-00942: table or view does not exist
when trying trying to query tables outside my schema.
EDIT
My apologies for not providing as much information as possible the first time around. I was under the impression this question was more simple.
I'm trying create a trigger on a table that changes some fields on a newly inserted row based on the existence of some data that may or may not be in a table that is in another schema.
The user account that I'm using to create the trigger does have the permissions to run the queries independently. In fact, I've had my trigger print the query I'm trying to run and was able to run it on it's own successfully.
I should also note that I'm building the query dynamically by using the EXECUTE IMMEDIATE statement. Here's an example:
CREATE OR REPLACE TRIGGER MAIN_SCHEMA.EVENTS
BEFORE INSERT
ON MAIN_SCHEMA.EVENTS REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
DECLARE
rtn_count NUMBER := 0;
table_name VARCHAR2(17) := :NEW.SOME_FIELD;
key_field VARCHAR2(20) := :NEW.ANOTHER_FIELD;
BEGIN
CASE
WHEN (key_field = 'condition_a') THEN
EXECUTE IMMEDIATE 'select count(*) from OTHER_SCHEMA_A.'||table_name||' where KEY_FIELD='''||key_field||'''' INTO rtn_count;
WHEN (key_field = 'condition_b') THEN
EXECUTE IMMEDIATE 'select count(*) from OTHER_SCHEMA_B.'||table_name||' where KEY_FIELD='''||key_field||'''' INTO rtn_count;
WHEN (key_field = 'condition_c') THEN
EXECUTE IMMEDIATE 'select count(*) from OTHER_SCHEMA_C.'||table_name||' where KEY_FIELD='''||key_field||'''' INTO rtn_count;
END CASE;
IF (rtn_count > 0) THEN
-- change some fields that are to be inserted
END IF;
END;
The trigger seams to fail on the EXECUTE IMMEDIATE with the previously mentioned error.
EDIT
I have done some more research and I can offer more clarification.
The user account I'm using to create this trigger is not MAIN_SCHEMA or any one of the OTHER_SCHEMA_Xs. The account I'm using (ME) is given privileges to the involved tables via the schema users themselves. For example (USER_TAB_PRIVS):
GRANTOR GRANTEE TABLE_SCHEMA TABLE_NAME PRIVILEGE GRANTABLE HIERARCHY
MAIN_SCHEMA ME MAIN_SCHEMA EVENTS DELETE NO NO
MAIN_SCHEMA ME MAIN_SCHEMA EVENTS INSERT NO NO
MAIN_SCHEMA ME MAIN_SCHEMA EVENTS SELECT NO NO
MAIN_SCHEMA ME MAIN_SCHEMA EVENTS UPDATE NO NO
OTHER_SCHEMA_X ME OTHER_SCHEMA_X TARGET_TBL SELECT NO NO
And I have the following system privileges (USER_SYS_PRIVS):
USERNAME PRIVILEGE ADMIN_OPTION
ME ALTER ANY TRIGGER NO
ME CREATE ANY TRIGGER NO
ME UNLIMITED TABLESPACE NO
And this is what I found in the Oracle documentation:
To create a trigger in another user's
schema, or to reference a table in
another schema from a trigger in your
schema, you must have the CREATE ANY
TRIGGER system privilege. With this
privilege, the trigger can be created
in any schema and can be associated
with any user's table. In addition,
the user creating the trigger must
also have EXECUTE privilege on the
referenced procedures, functions, or
packages.
Here: Oracle Doc
So it looks to me like this should work, but I'm not sure about the "EXECUTE privilege" it's referring to in the doc.
What you are experiencing is a feature of Oracle's security model. The entire point of using schemas is to control access to the data. The tables in my schema are mine, you cannot even see them until I grant you privileges on them.
The syntax is quite simple: the owner schema issues
grant select, insert on my_table to you
/
Alternatively an account with the GRANT ANY privilege (such as a DBA) can pass privileges on any user's objects.
grant select, insert on apc.my_table to you
/
The grantee can be either a user or a role. However, note that we can only build program units - stored procedures, views, triggers - using privileges which have been granted directly through to our user.
So, if you get the other schema owner to grant you the necessary privileges you will be able to build your trigger.
edit
When referencing an object in another schema we need to qualify the object with the schema name ....
insert into apc.whatever_table values ...
or else we need to create a synonym for it
create synonym whatever for apc.whatever_table;
I feel someone should add the obvious - the other schema's table must be qualified with the schema name or a private/public synonym is needed. I wonder if the original problem was merely a name resolution issue. If not, APC's answer is a good explanation of the Oracle security model.
You should execute this for every table and schema involved:
grant select on OTHER_SCHEMA_%.table_name to MAIN_SCHEMA;
Related
I have to 'Grant References' to a table in snowflake that gets 'create(d) or replace(d)' every morning as part of a daily ETL. So I have to run this every day to grant the right privilege to a certain role.
grant references on table example_table to role example_role;
I know the is a 'grant select on future', but this does not appear to exist for 'references'.
Anybody know the syntax or have an idea on how to get this grant to persist?
I cannot find anything in the snowflake docs.
CREATE OR REPLACE TABLE x (i int) COPY GRANTS
CREATE OR REPLACE VIEW x COPY GRANTS
AS
select 1 as i
Keyword is COPY GRANTS , it should work with all ddl command having create or replace syntax, of course if object does not exists it does not copy anything.
I have solved this type of issues by adding the grants as part of the ETL pipeline. For better maintenance, I created a table in snowflake which has two columns, table name and the grant required on that table. I wrote a stored procedure that looks at these tables and execute the grants. The stored procedure is part of the last step in the ETL pipeline
Snowflake's implementation of materialized views provides a number of unique characteristics which can be helpful because materialized views can improve the performance of queries that use the same subquery results repeatedly.
OR follow the syntax of these commands which may help.
"grant create materialized view on schema mydb.myschema to role myrole;"
OR
"grant operate on warehouse report_wh to role analyst with grant option"
I'm facing this problem, I created a database with multiple schemas other than public. When I connect with postgres user role (using pgAdmin, if I execute a create table statement, the table is not created but if I browse information_schema.tables that table is listed as table_schema=information_schema, even if in create statement I qualify the table with public schema. After that, if I create another table that reference the first one or just select from that table, postgres complains that the relation does not exixts. Then I have to drop the table to procede anyway.
If I connect with another user role, that have superuser property set, the table is created in public schema.
Is there any database property that I have to set or something other I have to do to avoid the above problem?
Please try creating tables with mentioning schema name like (create table schemaname.tablename(...)) may be it solve your problem.
May be you type
set search_path = information_schema;
to make some queries in the information_schema. But when you create the table, it was created in the information_schema. That was because the first schema name in set search_path is the default for creating objects.
I'm attempting to use an application role with a specific default schema to facilitate using SQL commands without schema designations, i.e. unqualified names. I need to create and work with 4+ complete sets of tables+views+etc. (each 50-100 objects) that all should work exclusively within their own schema.
To test it, I've created an application role with a suitable default schema. I then use 'sp_setapprole' to attach this role, and 'sp_unsetapprole' to revert to my previous role. This seems to be working, as SCHEMA_NAME() returns the expected schema name (and 'dbo' before 'set' and after 'unset').
While in my approle, I attempt to drop and create a table, and then insert a record into the created table. The drop/creation works fine, but the insert fails, as it attempts to insert the record into a table with a similar name in the 'dbo' schema (which it hasn't been given permission to do).
Why is it, that my (purposedly) unqualified table reference suddenly reverts to 'dbo' even though my approle dictated default schema is another ?
It also fails when I do a 'select', which again defaults to 'dbo'.
I have granted all possible permissions for my approle to the designated schema, and only SELECT to dbo.
Any help is appreciated.
Cheers
Lars
This is tricky because the object resolution is based on the default schema active when the batch is parsed. So a batch like this:
DECLARE #cookie varbinary(8000);
exec sp_setapprole a_role,'P#ssword', #fCreateCookie = true, #cookie = #cookie output
--go
select * from t
Will reference dbo.t, even if a_role has a different default schema. If you break it into two batches, then t will resolve to a.t.
Either that or you're using static SQL in a stored procedure, which is resolved relative to the procedure's schema, rather than any user's default_schema.
Apart from that, I don't think Application Roles are the right approach. You can accomplish this more simply by using impersonation than using Application Roles. EG grant the application users the right to impersonate a user who has rights only in a specific schema. Something like:
create role app_users
create user a_user without login with default_schema=a
grant select, insert, update, delete, execute on schema::a to a_user
grant impersonate on user::a_user to app_users
go
execute as user='a_user'
select * from t --resolves to a.t
select * from dbo.t --fails
revert
Every day in our datawarehouse (that will be dynamically changing) the tables are dropped and rebuilt. Also is it possible that some developer in our organisation will create more tables in that database.
Because of that I can not give permissions to the database that are persistent.
Question:
I want to make some kind of a job that runs every day, that lists all the table names (that are existing at that time) in a database like 'Select * FROM sys.tables'
Then I want the tables names as an input value to a script that runs trough all table names and places them in a script like :
GRANT SELECT TO [Tablename1] TO [ROLE_READALLTABLES Except 1 table],
GRANT SELECT TO [Tablenaam2] TO [ROLE_READALLTABLES Except 1 table]
and so go on in a loop until all existing tables are readable.
So all tables (except 1 table ) in the entire database should get the GRANT SELECT permission.
I have looked around all the related answers, but I cannot seem to get a good idea how to get this to work.
I hope someone can help me with this.
UPDATE
I use Microsoft SQL Server 2014, and I work through SQL Management Studio 2014
UPDATE 2 :
There is one exception. This table has schema [dbo]. like all other tables
You can use the db_datareader role to grant access to all tables generally, then a specific role with a DENY rule to exclude access to the one table that's the exception.
The steps would be roughly like this:
1) Create your "Read all except 1 role":
CREATE ROLE [ROLE_READALLEXCEPT1]
2) Create your "deny" role like so:
CREATE ROLE [ROLE_DENY]
GO
DENY SELECT, INSERT, UPDATE, DELETE ON myTable TO [ROLE_DENY]
GO
3) Then add your "except 1" role to it:
EXEC sp_addrolemember #rolename = 'ROLE_DENY', #membername = 'ROLE_READALLEXCEPT1'
4) Add your role to db_datareader:
EXEC sp_addrolemember #rolename = 'db_datareader', #membername = 'ROLE_READALLEXCEPT1'
The deny role should override db_datareader, and the net effect is that your role now has access to all tables (including new ones) except for those explicitly denied.
You can then add your users to "ROLE_READALLEXCEPT1" and they will have access to everything except the one exception table.
there is no information about the excluded table so i assume is always the same.I also assume that all the other tables are on the schema dbo; this is not a relevant constraint or limitation because the logic can be easily applied to more than one schema.
the easiest solution is granting permission at the schema level. move the single table on a separate schema with restricted permissions and grant full read on the whole schema where the user tables reside:
GRANT SELECT ON SCHEMA::dbo TO [relevant role/user];
now the developers can create all the table they feel like on the schema dbo and the permission are inherited by the schema.
should you need to grant access to more than one schema the permission are easily applied once and then every new table will get proper permission.
the huge pro of this solution is that it is fire and forget: once in place there is no maintenance, no jobs, no script to run daily/weekly/whatever.
this advantage is to be evaluated and weighted against the move of the excluded table (or the other way round: move the user tables): maybe is used by just a couple of internal applications so it is a quick patch or is used by a whole bunch of services accessible worldwide instead and that would be a nightmare.
I have a table with many records in Microsoft SQL Server 2008. Some of the records have boolean flag set. Others do not.
I want to a user to be able to ONLY see records where the flag is set. I made a view that uses a select statement to query these records and I gave the user read permissions on this view. But because the view selects from the original table SQL server is saying the user does not have enough permissions to look at the view.
The view and the table are in the same database.
How can I set permissions (either using a view or using some other method) so that the user can only see the subset of records from this table?
This works exactly as it should:
USE tempdb;
GO
CREATE TABLE dbo.MyFlags(a INT, flag BIT);
GO
INSERT dbo.MyFlags VALUES(1,0),(2,1),(3,1);
GO
CREATE VIEW dbo.vMyFlags
AS
SELECT a, flag FROM dbo.MyFlags WHERE flag = 1;
GO
CREATE LOGIN smudge WITH PASSWORD = 'floob', CHECK_POLICY = OFF;
GO
CREATE USER smudge FROM LOGIN smudge;
GO
GRANT SELECT ON dbo.vMyFlags TO smudge;
GO
EXECUTE AS user = 'smudge';
GO
-- from view (succeeds):
SELECT a,flag FROM dbo.vMyFlags;
GO
-- from table (error):
SELECT a,flag FROM dbo.MyFlags;
GO
Perhaps you missed a step there, or created or referenced objects without the correct schema prefix. Always, always, always use the schema prefix.