Permissions check postgresql function - postgresql

I have a function written in plpgsql that checks if user have permissions:
CREATE OR REPLACE FUNCTION check_permission(acc_id integer, permissions text[]) RETURNS boolean AS $$
BEGIN
SELECT DISTINCT p.name FROM person_role pr
JOIN role_permission rp ON pr.role_id = rp.role_id
JOIN permission p ON rp.permission_id = p.permission_id
WHERE account_id = acc_id ;
-- check
END;
$$ LANGUAGE plpgsql;
How can i check that select includes all permissions from permissions text[] argument?

You can unnest the array into a result set and from it subtract the role names from your result:
CREATE OR REPLACE FUNCTION check_permission(acc_id integer, permissions text[]) RETURNS boolean AS $$
SELECT NOT EXISTS (
SELECT unnest(permissions)
EXCEPT
SELECT DISTINCT p.name
FROM person_role pr
JOIN role_permission rp ON pr.role_id = rp.role_id
JOIN permission p ON rp.permission_id = p.permission_id
WHERE pr.account_id = acc_id
);
$$ LANGUAGE sql;
It might however better express the intent to use a boolean aggregate over the permission table and a subquery:
SELECT bool_and(EXISTS(
SELECT *
FROM person_role pr
JOIN role_permission rp ON pr.role_id = rp.role_id
WHERE rp.permission_id = p.permission_id
AND pr.account_id = acc_id
))
FROM permission p
WHERE p.name = ANY permissions -- or JOIN (SELECT unnest(permissions) AS name) USING (name)
Notice this will return NULL instead of TRUE when the permissions array is empty.

Related

Using PERFORM in recursive query function in PostgreSQL

I have a recursive query which I want to use in PostgreSQL function, and it should return a Boolean value.
CREATE OR REPLACE FUNCTION store.is_item(object1 VARCHAR(40), object2 VARCHAR(40))
RETURNS BOOLEAN AS $$
BEGIN
WITH RECURSIVE externals AS (
SELECT object_id, used_id
FROM store.obj_depend
WHERE external = true
), history AS (
SELECT content_id AS id
FROM store.minfos
WHERE id= $2
UNION
SELECT externals.used_id
FROM externals
INNER JOIN history ON history.id = externals.object_id
),
PERFORM (SELECT c.id FROM store.cinfo AS c WHERE c.id = $1 INNER JOIN history
ON c.id = history.id);
RETURN FOUND;
END;
$$ LANGUAGE plpgsql;
When I try this it gives asyntax error at or near SELECT error.
The PERFORM is plpgsql statement and cannot be used inside any SQL command.
You can use PERFORM like proposed #klin, but then CTE is used inside a subquery, and subquery returns one row every time. Then a FOUND variable should be every time true.
In this case is better to use auxiliary variable as targer:
CREATE OR REPLACE FUNCTION store.is_item(object1 VARCHAR(40), object2 VARCHAR(40))
RETURNS BOOLEAN AS $$
DECLARE r record;
BEGIN
WITH RECURSIVE
externals AS (SELECT object_id, used_id
FROM store.obj_depend
WHERE external = true)
history AS (SELECT content_id AS id
FROM store.minfos
WHERE id = object2
UNION
SELECT externals.used_id
FROM externals
INNER JOIN history ON history.id = externals.object_id)
SELECT c.id
FROM store.cinfo AS c
INNER JOIN history ON c.id = history.id
WHERE c.id = $1
INTO r;
RETURN FOUND;
END;
$$ LANGUAGE plpgsql;

POSTGRESQL- Query has no destination for result data

I'm new to postgres and to programming and I already searched for solution for this but I couldn't quite get it. I'm trying to make a function that will return information about all the customers on that particular country whenever I call the country. This is the error that pops up. I'm really sorry for asking this but I've been stuck here since yesterday.
ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
CONTEXT: PL/pgSQL function country(text) line 5 at SQL statement
Here is the function:
create or replace function country(text) returns text as $$
begin
select customer_id, customer.first_name, customer.last_name
from customer
inner join address on customer.address_id = address.address_id
inner join city on address.city_id = city.city_id
inner join country on city.country_id = country.country_id
where country = '$1';
end;
$$
language plpgsql;
If you are executing a select statement in a PL/pgSQL function, then you should place the result of the query in some variable(s) (= the destination). Then you work with the variable(s) in the function. You should also have a RETURN statement.
create or replace function country(text) returns text as $$
declare -- declare some variables
id integer;
fname text;
lname text;
begin
select customer_id, customer.first_name, customer.last_name
into id, fname, lname -- store query results in variables
from customer
inner join address on customer.address_id = address.address_id
inner join city on address.city_id = city.city_id
inner join country on city.country_id = country.country_id
where country = $1; -- don't quote parameter references
-- do something with the variables and return a value from the function
return format('%s: %s %s', id, upper(lname), fname);
end;
$$ language plpgsql;
Do note that the above only works if the query returns a single row. If the query returns multiple rows you can use a loop in the function. Even simpler, you can just return the results from the query like so:
create or replace function country(text)
returns table (id integer, first_name varchar, last_name varchar) as $$
begin
return query
select customer_id, customer.first_name, customer.last_name
from customer
inner join address on customer.address_id = address.address_id
inner join city on address.city_id = city.city_id
inner join country on city.country_id = country.country_id
where country = $1;
end;
$$ language plpgsql;
But like Evan Carroll said, unless you need a PL/pgSQL function to modify the data before returning it, you are better off with a simple view.
CREATE OR REPLACE FUNCTIOn func_process_client_contact(p_client_version_id bigint, p_publish_id bigint)
RETURNS TABLE(status_message character varying)
LANGUAGE plpgsql
AS $function$
declare
p_rowcount int;
BEGIN
-- validating input parameters : start
IF ( p_client_version_id IS NULL OR p_publish_id IS NULL )
THEN
RETURN;
END IF;
-- validating input parameters : end
WITH cte_target AS
(
SELECT
g.name AS clientname,
gcr.group_id AS clientid,
cr.id AS clientrelationid,
crc.id AS clientrelationcontactid,
crd.id AS clientrelationdesignation_id
FROM GROUPS g
JOIN group_client_relation gcr ON gcr.group_id =g.id
JOIN client_relation cr ON cr.id = gcr.client_relation_id
JOIN client_relation_contact crc ON crc.client_relation_id =cr.id
JOIN client_relation_designation crd ON cr.client_relation_designation_id =crd.id
)
SELECT * FROM cte_target ct WHERE ct.clientname='ABC';
GET DIAGNOSTICS p_rowcount = ROW_COUNT;
RAISE NOTICE 'returned % rows', p_rowcount;
IF ( p_rowcount=0 )
THEN
RETURN query SELECT 'hello' AS status_message;
ELSE
RETURN query SELECT 'Success' AS status_message;
END IF;
END
$function$
Please use below to get result of given function..
create or replace function country(in_parameter text,out out_res refcursor) as $$
begin
open out_res for
select customer_id, customer.first_name, customer.last_name
from customer
inner join address on customer.address_id = address.address_id
inner join city on address.city_id = city.city_id
inner join country on city.country_id = country.country_id
where country = '$1';
end;
$$
language plpgsql;
This is not normal for SQL. Normally, this would be a VIEW.
CREATE VIEW myView AS
SELECT customer_id, customer.first_name, customer.last_name
FROM customer
INNER JOIN address USING (address_id)
INNER JOIN city USING (city_id)
INNER JOIN country USING (country_id);
Then you do
SELECT * FROM myView WHERE country = ?
All of that said, if you insist on making this a function, and you shouldn't, you should make it a LANAGUAGE SQL and not LANGUAGE plppsql.
It worked for my colleague when she used OPEN MYCURS before the select query and RETURN MYCURS after the select query.
There might be some situations where we want to call another psql function from a function. If we just want to invoke the function and don't assign the return value to anything, then using select inner_function_call(); inside the main function will throw this error. As the hint suggests, use perform inner_function_call() instead.

How to check whether a stored procedure exists on PostgreSQL?

I'm pretty new to PostgreSQL and trying to learn PostgreSQL with the knowledge that I have on MS SQL Server & Oracle. I am looking for an equivalent of the following statement that we can use in MS SQL Server to check whether a Stored procedure exists or not, in PostgreSQL where SPName is your stored procedure's name.
SELECT 1 FROM sys.procedures WHERE Name = 'SPName')
SELECT 1 FROM sys.procedures WHERE object_id = OBJECT_ID(N'dbo.SPName')
SELECT EXISTS (
SELECT *
FROM pg_catalog.pg_proc
JOIN pg_namespace ON pg_catalog.pg_proc.pronamespace = pg_namespace.oid
WHERE proname = 'proc_name'
AND pg_namespace.nspname = 'schema_name'
)
If you've not created a specific schema then use public(pg_namespace.nspname = 'public')
OR
You can create a custom function to do the task like below:
create or replace function function_exists (sch text,fun text) returns boolean as
$$
begin
EXECUTE 'select pg_get_functiondef('''||sch||'.'||fun||'''::regprocedure)';
return true;
exception when others then
return false;
end;
$$ language plpgsql
and use :
select function_exists('public','function_name()')
Try like this:
SELECT EXISTS (
SELECT 1
FROM pg_proc JOIN pg_namespace on pg_proc.oid = pg_namespace.oid
WHERE pg_proc.proname = 'procedure name'
and pg_namespace.nspname = 'name' );
One-liner:
SELECT to_regproc('schema_name.proc_name') IS NOT NULL

How to execute dynamic query in PostgreSQL?

I am trying to execute dynamic query using PostgreSQL. I have a function with three parameters and need to suffix that parameters with some variables(to make view name) and need to retrieve rows from that variables(view) and return the result.
Example
create or replace function testing(abc varchar,def varchar,ghi varchar)
returns setof record as
$BODY$
Declare
temptable1 varchar :='temp1_';
temptable2 varchar :='temp2_';
viewname varchar :='view_';
Body
temptable1 := temptable1||abc;
temptable2 := temptable2||def;
viewname := viewname||ghi;
execute 'Drop table if exists'||temptable1;
execute 'Drop table if exists'||temptable2;
WITH cm
AS
(
SELECT "ssno","rlno",
DENSE_RANK() OVER(Partition by "ssno" Order By "rlno" )FoundIn
From viewname;
)
SELECT DISTINCT * INTO temptable1
FROM cm
WHERE FoundIn > 1;
SELECT DISTINCT cr."ssno", cdr."rlno"
INTO temptable2
FROM temptable1 l1
INNER JOIN viewname cr on l1."rlno" = cr."rlno"
ORDER BY "rlno";
/* Need to result should be display for below query */
SELECT DISTINCT cr.ssno AS Nos, cr.rlno, FoundIn,cr.Name, cr.Address,
from temptable1 l1
inner join viewname cr on l1.rlno = cr.rlno
order by "rlno"
end;
$BODY$
Language plpgsql;

How can you tell if a trigger is enabled in PostgreSQL?

My googling-fu is failing me. How to know if a PostgreSQL trigger is disabled or not?
The SQL below will do the work. It displays all triggers in your current database.
SELECT pg_namespace.nspname, pg_class.relname, pg_trigger.*
FROM pg_trigger
JOIN pg_class ON pg_trigger.tgrelid = pg_class.oid
JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace
If tgenabled is 'D', the trigger is disabled. All other values (documented here) indicate, that it is enabled in some way.
BTW. If you want to check the triggers for a specific table, the query is a bit shorter:
SELECT * FROM pg_trigger
WHERE tgrelid = 'your_schema.your_table'::regclass
The cast to the regclass type gets you from qualified table name to OID (object id) the easy way.
It's my first day with postresql, but I think you can check the trigger state via pg_trigger system table: http://www.postgresql.org/docs/current/static/catalog-pg-trigger.html
The columns you will need are tgrelid and tgenabled.
SELECT EXISTS (
SELECT tgenabled
FROM pg_trigger
WHERE tgname='your_unique_trigger_name' AND
tgenabled != 'D'
);
If you know the trigger name is unique the above will return true (t) if the your_unique_trigger_name trigger is enabled:
exists
--------
t
(1 row)
If disabled it would return false (f).
Starting from #tolgayilmaz reply I created a simply function to be used around my project
CREATE OR REPLACE FUNCTION trigger_exists_on_table(schema_name text, table_name text, trigger_name text)
RETURNS boolean AS
$body$
declare
_sql text;
_boolean_exists boolean;
begin
_sql := format('
SELECT EXISTS (
SELECT 1
FROM pg_trigger
WHERE tgrelid = ''%s.%s''::regclass AND tgname = ''%s'' AND tgenabled != ''D''
)
', schema_name, table_name, trigger_name);
execute _sql into _boolean_exists;
return _boolean_exists;
end
$body$
LANGUAGE plpgsql;