Allow only triggers to update a column - sql

I have two tables, t1(foo) and t2(bar), and a trigger on update t2.bar that updates t1.foo to something else.
At first I thought of just prohibiting any updates on t1 by using a trigger before update on t1.foo that always throws an exception. However, wouldn't that also block changes from the first trigger?
How do I do this?

Use a "daemon" role (a non-login role for this dedicated purpose) that owns the trigger function in combination with SECURITY DEFINER And grant the necessary privileges on t1 to it.
Details in these related questions:
Is there a way to disable updates/deletes but still allow triggers to perform them?
Allow insertion only from within a trigger

Related

Disable DELETE for a table in SQL Server

I'm currently working on creating a table for customers in an order management system for a course at university. The system relies on being able to retrieve order histories for the customers. Since we've chosen not to store these in a separate table, removing the option to delete rows from our customer table is essential.
How and where do I set this up in the CREATE statement? I suspect I'll have to create a rule about what should happen instead, but I'm not entirely sure about the specifics.
On SQL Server, you have the following options:
Deny object permissions. For example: DENY DELETE ON OBJECT::dbo.Customer TO db_datawriter;
Raise an error in a trigger: CREATE TRIGGER nodelete1 ON dbo.Customer INSTEAD OF DELETE AS RAISERROR('You can't delete from this table', 16, 10)
Rely on referential integrity without cascading updates/deletes. Note that this will only prevent deletion of a customer, if the customer has at least 1 order.
In my honest opinion, however, I think that this should be solved at the application level and not the database level. Even if using the techniques above, what would prevent someone from simply removing the trigger or grant the necessary permissions before DELETE'ing the records? Or simply dropping the entire table?
If you don't want your users to delete records from a table, simply make sure that your application does not allow them to do that. Anyone working directly with the database should know that issuing a DELETE statement could be dangerous - especially if you don't have a backup.
If the table is accessed only through an application, you can use a soft delete, to do that add a column to the table, for example IsDeleted, and check it in the software to see whether the row is live or deleted.
If the final users can access the DB, you can to change his/her login or group permission to remove the delete grant on that table.
i do it with a simple trigger on each table that i want disable delete
Create Trigger [dbo].[RollBackDelete]
ON [dbo].[Your Table Name]
INSTEAD OF DELETE
AS
BEGIN
ROLLBACK;
END
ofcourse if you have any key with cascade operation on delete or update it can not be work and you should set it to "No Action"
i hope this be be useful

Allow insertion only from within a trigger

I'm new to SQL programming, and I couldn't find an answer to this question online.
I'm working with pl/pgsql and I wish to achieve the following result:
I have a table A with certain attributes.
I am supposed to keep this table updated at any time - thus whenever a change was made that can affect A's values (in other tables B or C which are related to A) - a trigger is fired which updates the values (in the process - new values can be inserted into A, as well as old values can be deleted).
At the same time, I want to prevent from someone insert values into A.
What I want to do is to create a trigger which will prevent insertion into A (by returning NULL) - but I don't want this trigger to be called when I'm doing the insertion from another Trigger - so eventually - insertion to A will only be allowed from within a specific trigger.
As I said before, I'm new to SQL, and I don't know if this is even possible.
Yes, totally possible.
1. Generally disallow UPDATE to A
I would operate with privileges:
REVOKE ALL ON TABLE A FROM public; -- and from anybody else who might have it
That leaves superusers such as postgres who ignore these lowly restrictions. Catch those inside your trigger-function on A with pg_has_role():
IF pg_has_role('postgres', 'member') THEN
RETURN NULL;
END IF;
Where postgres is an actual superuser. Note: this catches other superusers as well, since they are member of every role, even other superusers.
You could catch non-superusers in a similar fashion (alternative to the REVOKE approach).
2. Allow UPDATE for daemon role
Create a non-login role, which is allowed to update A:
CREATE ROLE a_update NOLOGIN;
-- GRANT USAGE ON SCHEMA xyz TO a_update; -- may be needed, too
GRANT UPDATE ON TABLE A TO a_update;
Create trigger functions on tables B and C, owned by this daemon role and with SECURITY DEFINER. Details:
Is there a way to disable updates/deletes but still allow triggers to perform them?
Add to the trigger function on A:
IF pg_has_role('postgres', 'member') THEN
RETURN NULL;
ELSIF pg_has_role('a_update', 'member') THEN
RETURN NEW;
END IF;
For simple 1:1 dependencies, you can also work with foreign key constraints (additionally) using ON UPDATE CASCADE.

Difference between FOR and AFTER triggers?

What's the difference between FOR and AFTER triggers?
There is no difference, they do the same thing.
CREATE TRIGGER trgTable on dbo.Table FOR INSERT,UPDATE,DELETE
Is the same as
CREATE TRIGGER trgTable on dbo.Table AFTER INSERT,UPDATE,DELETE
An INSTEAD OF trigger is different, and fires before and instead of the insert and can be used on views, in order to insert the appropriate values into the underlying tables.
#Ben is absolutely right.
Here is MSDN article Exploring SQL Server Triggers
A paragraph from the article:
That syntax is also acceptable in older versions of SQL Server. However, now that there are two types of triggers in SQL Server 2000, I prefer to refer to FOR triggers as AFTER triggers. Thus, for the remainder of this article I will refer to either AFTER or INSTEAD OF triggers.
Like the AFTER trigger you saw earlier, this trigger prevents changes from being made to the lastname field. However, it implements this business rule differently than the previous example. Because the INSTEAD OF trigger fires in place of the UPDATE statement, the INSTEAD OF trigger then evaluates if the business rule test passes or not. If the business rule test passes, in order for the update to occur the INSTEAD OF trigger must explicitly invoke the UPDATE statement again.
AFTER specifies that the DML trigger is fired only when all operations specified in the triggering SQL statement have executed successfully. All referential cascade actions and constraint checks also must succeed before this trigger fires.
AFTER is the default when FOR is the only keyword specified.
AFTER triggers cannot be defined on views.
INSTEAD OF
Specifies that the DML trigger is executed instead of the triggering SQL statement, therefore, overriding the actions of the triggering statements. INSTEAD OF cannot be specified for DDL or logon triggers.
https://learn.microsoft.com/en-us/sql/t-sql/statements/create-trigger-transact-sql

Is it Possible to Enforce Read-Only Fields in SQL Server 2008 R2?

I'd want to prevent UPDATE querys that write over ceartain fields in my database. I also want to prevent UPDATE or DELETE querys of any kind on certain tables, as these tables contain permanent information that needs to persist indefinitely.
Can these configurations be implemented in SQL Server Management Studio?
In order to prevent updates on certain fields, you'll probably have to have an AFTER UPDATE trigger on that table that would check if any of the "read-only" fields is about to be updated; if so, abort the transaction.
CREATE TRIGGER triggerName
ON dbo.YourTable AFTER UPDATE
AS
IF UPDATE(somefield)
ROLLBACK TRANSACTION
To prevent certain users access to certain tables, just don't grant those users (or a database role they belong to) the UPDATE and/or DELETE permission on those tables.
Remove all permissions for read only tables. No-one can make changes then
Consider stored procs or views to control writes, again remove direct permissions
See marc_s answer
If your users connect as dbo or sa then there is nothing you can do that is effective. Users can disable trigegrs or delete them. With sa permissions are never checked anyway.
#marc_s is right, if you want to prevent changes in some fields you should use a trigger or limit the user's permissions if you are the dba. In any case, instead of an AFTER trigger i would use an INSTEAD OF trigger, so it wouldn't be necessary to rollback the transaction.
CREATE TRIGGER triggerName
ON dbo.YourTable INSTEAD OF UPDATE, INSERT
AS
IF UPDATE(somefield)
-- do nothing or whatever
In any case, if you find this answer correct, please accept the answer provided by #marc_s, this is just something extra over that answer

MSSQL: Disable triggers for one INSERT

This question is very similar to SQL Server 2005: T-SQL to temporarily disable a trigger
However I do not want to disable all triggers and not even for a batch of commands, but just for one single INSERT.
I have to deal with a shop system where the original author put some application logic into a trigger (bad idea!). That application logic works fine as long as you don't try to insert data in another way than the original "administration frontend". My job is to write an "import from staging system" tool, so I have all data ready. When I try to insert it, the trigger overwrites the existing Product Code (not the IDENTITY numeric ID!) with a generated one. To generate the Code it uses the autogenerated ID of an insert to another table, so that I can't even work with the ##IDENTITY to find my just inserted column and UPDATE the inserted row with the actual Product Code.
Any way that I can go to avoid extremly awkward code (INSERT some random characters into the product name and then try to find the row with the random characters to update it).
So: Is there a way to disable triggers (even just one) for just one INSERT?
You may find this helpful:
Disabling a Trigger for a Specific SQL Statement or Session
But there is another problem that you may face as well.
If I understand the situation you are in correctly, your system by default inserts product code automatically(by generating the value).
Now you need to insert a product that was created by some staging system, and for that product its product code was created by the staging system and you want to insert it to the live system manually.
If you really have to do it you need to make sure that the codes generated by you live application in the future are not going to conflict with the code that you inserted manually - I assume they musty be unique.
Other approach is to allow the system to generate the new code and overwrite any corresponding data if needed.
You can disable triggers on a table using:
ALTER TABLE MyTable DISABLE TRIGGER ALL
But that would do it for all sessions, not just your current connection.. which is obviously a very bad thing to do :-)
The best way would be to alter the trigger itself so it makes the decision if it needs to run, whether that be with an "insert type" flag on the table or some other means if you are already storing a type of some sort.
Rather than disabling triggers can you not change the behaviour of the trigger. Add a new nullable column to the table in question called "insertedFromImport".
In the trigger change the code so that the offending bit of the trigger only runs on rows where "insertedFromImport" is null. When you insert your records set "insertedFromImport" to something non-null.
Disable the trigger, insert, commit.
SET IDENTITY_INSERT Test ON
GO
BEGIN TRAN
DISABLE TRIGGER trg_Test ON Test
INSERT INTO Test (MyId, MyField)
VALUES (999, 'foo')
ENABLE TRIGGER trg_Test ON Test
COMMIT TRAN
SET IDENTITY_INSERT Test OFF
GO
Can you check for SUSER_SNAME() and only run when in context of the administration frontend?
I see many things that could create a problem. First change the trigger to consider multiple record imports. That may probably fix your problem. DO not turn off the trigger as it is turned off for everyone not just you. If you must then put the database into single user user mode before you do it and do your task during off hours.
Next, do not under any circumstances ever use ##identity to get the value just inserted! USe scope_identity instead. ##identity will return the wrong value if there are triggers onthe table that also do inserts to other tables with identity fields. If you are using ##identity right now through your system (since we know your system has triggers), your abosolute first priority must be to immediately find and change all instances of ##identity in your code. You can have serious data integrity issues if you do not. This is a "stop all work until this is fixed" kind of problem.
As far as getting the information you just inserted back, consider creating a batchid as part of you insert and then adding a column called batchid (which is nullable so it won't affect other inserts)to the table. Then you can call back what you inserted by batchid.
If you insert using BULK INSERT, you can disable triggers just for the insert.
I'm pretty sure bulk insert will require a data file on the file system to import so you can't just use T-SQL.
To use BULK INSERT you need INSERT and ADMINISTRATOR BULK OPERATION permissions.
If you disable triggers or constraints, you'll also need ALTER TABLE permission.
If you are using windows authentication, your windows user will need read access from the file. if using Mixed Mode authentication, the SQl Server Service account needs read access from the file.
When importing using BULK IMPORT, triggers are disabled by default.
More information: http://msdn.microsoft.com/en-us/library/ms188365.aspx