How to call DROP USER from pl/pgsql? - postgresql

How to call DROP USER from Pl/PgSql (see example) ?
CREATE PROCEDURE myfunc()
LANGUAGE PLPGSQL AS
$$
DECLARE
super_users TEXT[];
ldap_users TEXT[];
u TEXT;
BEGIN
super_users := ARRAY(SELECT usename::TEXT FROM pg_catalog.pg_user WHERE usesuper);
ldap_users := ARRAY(SELECT uid::TEXT FROM ldap_users);
FOREACH u IN ARRAY ldap_users LOOP
IF (u <> 'postgres' AND u <> ALL(super_users)) THEN
DROP USER IF EXISTS u;
END IF;
END LOOP;
END;
$$;
It leads to error that "role u does not exist"...
IMHO PL/PGSQL does not treat u as a variable, but as a name. And DROP USER... is not SQL but some extension. How to do it? Maybe some system function? Or special syntax to substitute u?
EDIT:
My solution (just found):
DECLARE
stm TEXT;
...
BEGIN
...
stm := 'DROP USER IF EXISTS "' || u '"';
EXECUTE stm;
...
It seems to work. Maybe there is other solutions? More canonical?

You need dynamic SQL for this:
execute format('DROP USER IF EXISTS %I', u);

Try this:
DROP OWNED BY user;
DROP ROLE user;
DROP USER user;
It works for me.
P.d: After checking SOF, you can try:
REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM user;
REVOKE ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public FROM user;
REVOKE ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA public FROM user;
DROP USER user;
Cheers mate.

Related

postgres: Using trigger function to remove user from a group

I have a trigger function that gets a person's username from their first and last name, and then drops the user with that username from a group with privileges, essentially locking that person out of the database.
returns "trigger" AS'
DECLARE
uname varchar(255);
begin
uname = (SELECT CONCAT(LOWER(first_name), LOWER(last_name)) FROM members WHERE library_card_id = (SELECT members_library_card_id FROM borrowed_books WHERE fine_id IS NOT NULL));
alter group members drop user uname;
end;
' LANGUAGE 'plpgsql';
CREATE TRIGGER block_member_if_fine
AFTER UPDATE on borrowed_books
FOR EACH ROW
EXECUTE PROCEDURE f_block_member_if_fine();
However, when I use the trigger function, I get an error role "uname" does not exist
Is there a way to use uname as a variable? Or maybe there's a different way of dropping a user with a trigger function?
Try this :
CREATE OR REPLACE PROCEDURE f_block_member_if_fine()
RETURNS "trigger" LANGUAGE 'plpgsql' AS
$$
DECLARE
uname varchar(255);
BEGIN
SELECT CONCAT(LOWER(m.first_name), LOWER(m.last_name))
INTO uname
FROM members AS m
WHERE m.library_card_id = NEW.members_library_card_id
AND NEW.fine_id IS NOT NULL;
IF FOUND
THEN
EXECUTE 'ALTER GROUP members DROP USER '|| quote_nullable(uname) ;
END IF ;
RETURN NEW ;
END ;
$$
CREATE TRIGGER block_member_if_fine
AFTER UPDATE on borrowed_books
FOR EACH ROW
EXECUTE PROCEDURE f_block_member_if_fine();

Using a variable on a PostgreSQL function to drop a schema

I'm trying to create a function on PostgreSQL, and I have some problem to use a local variable. Here's my code :
DECLARE query RECORD;
DECLARE schema_name TEXT;
BEGIN
FOR query IN SELECT * FROM context WHERE created_at + make_interval(days => duration) <= CURRENT_TIMESTAMP LOOP
SELECT lower(quote_ident(query.title)) INTO schema_name;
DROP SCHEMA schema_name CASCADE;
DELETE FROM context WHERE id = query.id;
END LOOP;
RETURN 1;
END;
$$ LANGUAGE plpgsql;
The select and delete queries work fine, and I've made a test returning the value of schema_name variable, and it's OK.
My problem is with this line :
DROP SCHEMA schema_name CASCADE;
I get an error as "the schema 'schema_name' doesn't exist".
I'd really appreciate any ideas for how to use this variable to do the drop query.
You need dynamic SQL for this:
DECLARE
query RECORD;
BEGIN
FOR query IN SELECT id, lower(title) as title
FROM context
WHERE created_at + make_interval(days => duration) <= CURRENT_TIMESTAMP
LOOP
execute format('DROP SCHEMA %I CASCADE', query.title);
DELETE FROM context WHERE id = query.id;
END LOOP;
RETURN 1;
END;
$$ LANGUAGE plpgsql;
I also removed the unnecessary SELECT statement to make the title lower case, this is better done in the query directly.
Also: variable assignment is faster with := then with select, so:
schema_name := lower(quote_ident(query.title));
would be better if the variable was needed.

Postgresql creating users with a function

I'm currently not able to create a postgresql database user within a function.
Background:
I have a Java Swing application and my goal is to develop a menu to create, alter and delete database users. To make it a bit more secure I created a role "usermanagement" and only members of this role are allowed to use the function to create users. The role also contains the right "createuser"
The query runs without any problems, but it does not create a new user... So i don't know what's wrong with it.
This is how i try to use my function:
SELECT create_databaseuser(v_username := 'thisname' ,v_password := 'pwpwpw');
Can anyone help?
Here is my code:
-- Function: public.create_databaseuser(text, text)
-- DROP FUNCTION public.create_databaseuser(text, text);
CREATE OR REPLACE FUNCTION public.create_databaseuser(
v_username text,
v_password text)
RETURNS numeric AS
$BODY$
DECLARE
r_id numeric;
BEGIN
--CREATE ROLE v_username LOGIN
--PASSWORD 'v_password' NOSUPERUSER INHERIT NOCREATEDB NOCREATEROLE NOREPLICATION;
EXECUTE 'CREATE USER ' || v_username || ' WITH PASSWORD ' || v_password;
-- Alternative:CREATE ROLE v_username LOGIN PASSWORD v_password NOSUPERUSER INHERIT NOCREATEDB NOCREATEROLE NOREPLICATION;
return 1;
-- Simple Exception
EXCEPTION
WHEN others THEN
RETURN 0;
END;
$BODY$
LANGUAGE plpgsql VOLATILE SECURITY DEFINER
COST 100;
ALTER FUNCTION public.create_databaseuser(text, text)
OWNER TO postgres;
Note the STRICT (returns NULL on NULL input), uses FORMAT() to help against SQL injection issues, and quotes the inputs properly. The input argument "v_username" was revised to be of type NAME, to match the type in pg_catalog.pg_roles.
DROP FUNCTION IF EXISTS public.create_databaseuser(NAME, TEXT);
CREATE OR REPLACE FUNCTION public.create_databaseuser(
v_username NAME,
v_password TEXT)
RETURNS smallint AS
$BODY$
DECLARE
BEGIN
EXECUTE FORMAT('CREATE ROLE "%I" LOGIN PASSWORD ''%L''', v_username, v_password);
RETURN 1;
-- Simple Exception
EXCEPTION
WHEN others THEN
RETURN 0;
END;
$BODY$
LANGUAGE plpgsql STRICT VOLATILE SECURITY DEFINER
COST 100;
ALTER FUNCTION public.create_databaseuser(NAME, TEXT) OWNER TO postgres;
select rolname from pg_catalog.pg_roles order by 1;
SELECT create_databaseuser(v_username := 'thisname' ,v_password := 'pwpwpw');
select rolname from pg_catalog.pg_roles order by 1;

How to grant permission using FUNCTION in PostgreSQL 9.4.5

I need to grant permissions to database users. i need to use FUNCTION to perform this.
grant all on schema schema1 to user1;
grant all on schema schema1 to user2;
grant all on schema schema1 to user3;
schema names and user names should be picked up from these sql query output:
select nspname from pg_namespace;
schema1
schema2
schema3
select usename from pg_user;
user1
user2
user3
could someone help me how to write a function to achive this ? i am using Postgresql 9.4.5.
Many Thanks,
It could be something like this:
CREATE OR REPLACE FUNCTION grant_all()
RETURNS VOID AS
$$
DECLARE
user TEXT;
schema TEXT;
BEGIN
FOR user IN (SELECT usename FROM pg_user)
LOOP
FOR schema IN (SELECT nspname FROM pg_namespace)
LOOP
EXECUTE format('GRANT ALL ON SCHEMA %s to %s', schema, user);
RAISE NOTICE 'Granted all on % to %', schema, user;
END LOOP;
END LOOP;
END
$$
LANGUAGE plpgsql;

postgresql stored procedure with query on several schema

After some answer on a previous question (request over several schema), I try to write a stored procedure to select tables for several schemas (Each user have a schema).
create or replace public.select_simulations() returns setof simulation as $$
declare
users pg_user%ROWTYPE;
simu simulation%ROWTYPE;
begin
for users in select usename from pg_user where usename <> 'postgres' loop
for simu in select id, name from (users.usename).simulation loop
return next simu;
end loop;
end loop;
end;
$$
but it doesn't accept the (users.usename).simulation, and without the parenthesis it produced an error (seems to search a sub field, not a schema)...
So what is the correct syntax to tell that users.usename is a schema name ?
Thank you for your help!
You could take a look at the for-in-execute control structure:
FOR record_or_row IN EXECUTE text_expression LOOP
statements
END LOOP [ label ];
http://www.postgresql.org/docs/8.1/static/plpgsql-control-structures.html#PLPGSQL-RECORDS-ITERATING
Something like:
...
for users in select usename from pg_user where usename <> 'postgres' loop
for simu in execute 'select id, name from '||quote_ident(users.usename)||'.simulation' loop
return next simu;
end loop;
end loop;
...