Restrict INSERT to specific value in given column for certain user - sql

Is there a possibility, to restrict, that certain user can insert only specified value in some column?
For example table
test (id integer, value text)
and user 'restricted_user' could only INSERT 1 in column id (any in the other columns)

Sure, use Row Level Security:
ALTER TABLE test ENABLE ROW LEVEL SECURITY;
GRANT INSERT ON test TO restricted_user;
CREATE POLICY restr_ins ON test
FOR INSERT TO restricted_user
WITH CHECK (id = 1);
You'd have to add appropriate policies for other users that are supposed to work with the table, else they can do nothing with the table.

Row Level Security (as shown by Laurenz) is one option. Using a VIEW and with check option is another solution:
create view restricted_test
as
select *
from test
where id = 1
with check option;
Then disallow inserts into the table directly:
revoke insert,update on test from restricted_user;
And only allow to insert into the view:
grant insert,update on restricted_test to restricted_user;

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

Insert into a table containing only identity column

I have the following table :
CREATE TABLE Seq2 (val INT NOT NULL IDENTITY);
How to populate this table knowing that I tried this :
INSERT INTO Seq2(val) VALUES (1)
I have the following error :
Cannot insert explicit value for identity column in table 'Seq2' when
IDENTITY_INSERT is set to OFF.
Having such a table seems completely pointless, if I must say. If the table has only an IDENTITY then it effectively holds no meaning, so there's no point it being there.
That being said, if you did have such a table, you can INSERT values into the IDENTITY using DEFAULT VALUES:
INSERT INTO dbo.Seq2
DEFAULT VALUES;
INSERT INTO dbo.Seq2
DEFAULT VALUES;
With a new table, this would create rows with the values 1 and 2.
If you want to explicitly INSERT values into the table, then you're better off remove the IDENTITY option. Considering this is a new table, just DROP it and recreate it with the IDENTITY property:
DROP TABLE dbo.Seq2;
GO
CREATE TABLE Seq2 (val INT NOT NULL);
Having a table with a single IDENTITY column, that you're then going to define the results for really is pointless. Either don't use IDENTITY and define the values, or use IDENTITY and let SQL Server handle it.
SET IDENTITY_INSERT Seq2 ON
INSERT INTO Seq2(val)VALUES (1)
SET IDENTITY_INSERT Seq2 OFF
Simply, enable IDENTITY_INSERT for the table. That looks like this:
SET IDENTITY_INSERT IdentityTable ON
INSERT INTO Seq2(val) VALUES (1)
SET IDENTITY_INSERT IdentityTable OFF
Keep in mind :
It can only be enabled on one table at a time. If you try to enable
it on a second table while it is still enabled on a first table SQL
Server will generate an error.
When it is enabled on a table you must specify a value for the
identity column.
The user issuing the statement must own the object, be a system
administrator (sysadmin role), be the database owner (dbo) or be a
member of the db_ddladmin role in order to run the command.
SELECT SCOPE_IDENTITY() // to get last identity value generated in the same session and scope
SELECT ##IDENTITY // to get the last identity vaue generated in a session irrespective of scope

Row level policy doesn't work on my table

I have a table which I'm trying to apply a policy on, the setup looks something like this:
create role anonymous nologin;
create schema api;
create schema private;
create table private.mytable(
id serial primary key,
description text default ''
);
create view api.mytable as select * from private.mytable;
insert into api.mytable (description) values ('row 1'), ('row 2');
grant usage on schema api to anonymous;
grant select on api.mytable to anonymous;
alter table private.mytable enable row level security;
create policy mytable_policy on private.mytable
for select
using (null);
When I set the role to anonymous and select all records from mytable:
set role anonymous;
select * from api.mytable;
I excpect no rows to be returned since my expression in the using clause in the policy is null but I get everything.
I tried different postgresql versions (9.5, 9.6, 10.3) but they all have the same behaviour, am I doing something wrong here?
update
https://stackoverflow.com/a/33863371/5315974
RLS won't work with views like that. You can use RLS for views though it is limited.
what you can do is
alter view api.mytable owner to anonymous ;

Trigger that creates a row in another table that is referenced by current table

I have 2 tables User and Account. I'd like to have a trigger that creates an account automatically when a user is created. Here is my code:
alter trigger Add_user on [user] for insert as
begin
insert into [account] (name) values ('Main')
declare #newAccountId int, #insertedId int
set #newAccountId = (select scope_identity())
set #insertedId = (select id from inserted)
update [user]
set accountId = #newAccountId
where id = #insertedId
end
I want to have AccountId in the User table be not null however when I try and create a new user it won't let me and I get an error complaining about the not null AccountId column :(
If you make [user].AccountId nullable, it should work.
Also consider following things:
does [account] table contain only column "name"? I.e. is it global
for all users? Then why create new account for each user? If it's
user-specific then add [account].[userId] column.
I would recommend to write stored procedure instead of trigger (first create
account record then user record), it's more explicit and safe. Be
careful with triggers, it's likely to be a surprise for other
developers that inserting user also creates account.

triggers in different schemas

I am new to SQL Server.
I have to write a trigger for inserting and updating table in different schema in MS SQL.
Example:
TEMP1 table in one Schema
TEMP2 table in another Schema
How can this be done?
As long the SCHEMAs have the same owner (The AUTHORIZATION bit in CREATE SCHEMA) you'd simply refer to the objects using 2 part names.
See CREATE TRIGGER too
create trigger MyTrigger on Schema1.Table1
for insert
as
set nocount on
insert Schema2.Table2 (...)
select (..) from inserted
go
Not sure I understand the problem completely, but basic syntax would look like this:
create trigger MyTrigger on Schema1.Table1
after insert, update
as
insert Schema2.Table2 values(1, 'test', ...)
update Schema3.Table3
set Name = 'XX'
where Id = 1
go
You have to create multiple triggers to handle different events on different tables.
Refer to CREATE TRIGGER (Transact-SQL).