How to deny delete on a table for all users - sql

Within SQL Server 2005, is there a way, with a single statement, to deny delete on rows
in a particular table for all users of the database?

try this:
CREATE TRIGGER yourTriggerName ON YourTableName
INSTEAD OF DELETE
AS
ROLLBACK
RAISERROR('ERROR, DELETEs not permitted in YourTableName!!!',16,1)
RETURN
go
working sample:
CREATE TABLE XYZ (RowID int)
INSERT XYZ VALUES(1)
INSERT XYZ VALUES(2)
go
CREATE TRIGGER yourTriggerName ON XYZ
INSTEAD OF DELETE
AS
ROLLBACK
RAISERROR('ERROR, DELETEs not permitted in XYZ!!!',16,1)
RETURN
go
delete XYZ
OUTPUT:
Msg 50000, Level 16, State 1, Procedure yourTriggerName, Line 6
ERROR, DELETEs not permitted in XYZ!!!
Msg 3609, Level 16, State 1, Line 1
The transaction ended in the trigger. The batch has been aborted.

Kind of extreme, but you can look at an INSTEAD OF DELETE trigger to ignore the deletions. You could raise an error similar to 'delete access ... denied'
Obviously users with db_owner access privileges could drop the trigger

if you want to row based restriction create a table (for lock) and insert ids of rows that you want to protect. and create a relation.

Only triggers (or refactor your security/permission model)
One example where permissions won't help: If you have stored procs that delete rows, and both the proc/table have the same owner, table permissions will not be checked, even DENY. See ownership chaining. So users can delete or change data without any rights on the table at all.

Related

Subqueries are not allowed in this context

I want to prevent further duplicates from being added to my table while allowing existing duplicates to remain. I thought I could accomplish this using a filtered index as follows.
But when I execute the following query:
CREATE UNIQUE INDEX IX_Account
ON Holdings(Account)
WHERE Account NOT IN (select Account from Holdings)
I get the following error:
Msg 1046, Level 15, State 1, Line 57
Subqueries are not allowed in this context. Only scalar expressions are allowed.
How can I prevent further duplicates from being added?
You can't have your cake and eat it.
Either
decide that your data should have integrity and purge the duplicated before adding the unique index (filtering it for the reason you mention does not make sense)
or
enforce your logic with an insert trigger:
create trigger no_more_duplicates on Holdings
after insert as
if exists
(
select 1
from inserted
where inserted.Account IN (select Account from Holdings)
)
raiserror('Cannot add duplicates',16,0)
end -- trigger
This trigger's a bit dumb, it will not prevent duplicates on a multiple-row insert, nor will it let the nonduplicate ones be saved. Yet, it's enough that you get the picture.
raiserror in a trigger will not automatically rollback the transaction, but throw will. Alternatively you can raiserror and rollback.
Also with an AFTER trigger the data in the INSERTED virtual table is already present in the table. So a trigger would need to look like:
use tempdb
drop table if exists Holdings
create table Holdings(id int primary key, Account int)
go
create or alter trigger no_more_duplicates on Holdings
after insert as
begin
if exists
(
select 1
from inserted
where inserted.Account IN (select Account from Holdings where id <> inserted.id)
)
begin
throw 60000, 'Cannot add duplicates', 1 ;
--raiserror('Cannot add duplicates',16,1)
end;
end -- trigger
go
insert into Holdings(id,Account) values (1,1)
go
insert into Holdings(id,Account) values (2,1)
go
select * from holdings

I can't run inserts in firebird 2.5, Table Unknown

I'm trying to create a temporary table to save some codes, but when I try to insert a code it throws me the following error as if the table did not exist:
can't format message 13:796 -- message file C:\Windows\firebird.msg
not found. Dynamic SQL Error. SQL error code = -204. Table unknown.
TEMPCODES. At line 1, column 13.
These are the lines that I try to run:
create global temporary table TEMPCODES
(
codigo varchar(13)
)
on commit delete rows;
insert into TEMPCODES values('20-04422898-0');
Why can't it find the table if I'm creating it before?
In Firebird, you cannot use a database object in the same transaction that created it. You need to commit before you can use the table.
In other words, you should use:
create global temporary table TEMPCODES
(
codigo varchar(13)
)
on commit delete rows;
commit;
insert into TEMPCODES values('20-04422898-0');
Also, it is important to realise that global temporary tables (GTT) are intended as permanent objects. The idea is to create a GTT once, and then use it whenever you need it. The content of a GTT is only visible to the current transaction (on commit delete rows) or to the current connection (on commit preserve rows). Creating a GTT on the fly is not the normal usage pattern for GTTs.

INSERTED table really exists

Why I can use this select:
SELECT 1 FROM INSERTED
into a trigger, but not run like another select?
I got this error:
Msg 208, Level 16, State 1, Line 1
Invalid object name 'INSERTED'.
It works in this statement (that's inside a trigger):
IF EXISTS(SELECT 1 FROM INSERTED) AND
NOT EXISTS(SELECT 1 FROM DELETED)
BEGIN
-- AFTER INSERT
UPDATE VEIC
SET
VEIC.FLAG = 'I'
FROM
DBVEICULO VEIC
INNER JOIN INSERTED INS ON INS.ID_VEICULO = VEIC.ID_VEICULO
Trigger > http://pastebin.com/9Dh4TUPc
As per the documentation - Create Trigger (Transact-SQL):
DML triggers use the deleted and inserted logical (conceptual) tables. They are structurally similar to the table on which the trigger is defined, that is, the table on which the user action is tried. The deleted and inserted tables hold the old values or new values of the rows that may be changed by the user action. For example, to retrieve all values in the deleted table, use:
So the two tables, INSERTED and DELETED, only exists in the context of a trigger.

Set trigger order usage

we can set order option of trigger to first, last or none by use sp_settriggerorder command. I want to know what's significance of set trigger order. and when necessity using this option?
I use SQL Server 2008 R2.
One purpose would be if you want one trigger to perform some form of sanity checks before other triggers (which may have expensive actions) fire. If the first trigger causes a ROLLBACK to occur, the other triggers aren't fired:
create table T (
ID int not null
)
go
create trigger T1
on T
after insert
as
RAISERROR('T1',10,1) WITH NOWAIT
go
create trigger T2
on T
after insert
as
RAISERROR('T2',10,1) WITH NOWAIT
go
create trigger T3
on T
after insert
as
RAISERROR('T3',10,1) WITH NOWAIT
go
sp_settriggerorder 'T1','First','Insert'
go
sp_settriggerorder 'T3','Last','Insert'
go
insert into T(ID) values (1)
--T1
--T2
--T3
--(1 row(s) affected)
go
alter trigger T1
on T
after insert
as
RAISERROR('T1',10,1) WITH NOWAIT
ROLLBACK TRANSACTION
go
insert into T(ID) values (2)
--T1
--Msg 3609, Level 16, State 1, Line 1
--The transaction ended in the trigger. The batch has been aborted.
It specifies in what order AFTER triggers fire when multiple triggers apply to a SQL statement. You can specify only one first or last trigger for a specific table, database or server.

SQL: Why does a CREATE TRIGGER need to be preceded by GO

When making a SQL script to create a trigger on a table, I wanted to check that the trigger doesn't already exist before I create it. Otherwise the script cannot be run multiple times.
So I added a statement to first check whether the trigger exists. After adding that statement, the CREATE TRIGGER statement no longer works.
IF NOT EXISTS (SELECT name FROM sysobjects
WHERE name = 'tr_MyTable1_INSERT' AND type = 'TR')
BEGIN
CREATE TRIGGER tr_MyTable1_INSERT
ON MyTable1
AFTER INSERT
AS
BEGIN
...
END
END
GO
This gives:
Msg 156, Level 15, State 1, Line 5
Incorrect syntax near the keyword 'TRIGGER'.
The solution would be to drop the existing trigger and then create the new one:
IF EXISTS (SELECT name FROM sysobjects
WHERE name = 'tr_MyTable1_INSERT' AND type = 'TR')
DROP TRIGGER tr_MyTable1_INSERT
GO
CREATE TRIGGER tr_MyTable1_INSERT
ON MyTable1
AFTER INSERT
AS
BEGIN
...
END
GO
My question is: why is the first example failing? What is so wrong with checking the trigger exists?
Certain statements need to be the first in a batch (as in, group of statements separated by GO ).
Quote:
CREATE DEFAULT, CREATE FUNCTION, CREATE PROCEDURE, CREATE RULE, CREATE SCHEMA, CREATE TRIGGER, and CREATE VIEW statements cannot be combined with other statements in a batch. The CREATE statement must start the batch. All other statements that follow in that batch will be interpreted as part of the definition of the first CREATE statement.
It's simply one of the rules for SQL Server batches (see):
http://msdn.microsoft.com/en-us/library/ms175502.aspx
Otherwise you could change an object, say a table, and then refer to the change in the same batch, before the change was actually made.
Schema changes should always be seperate batch calls...I am guessing they do it to gaurantee your SELECT will succeed, if you modify schema in the same batch they may not be able to gaurantee that. Just a guess...