Use a postgres extension as a non-privileged user - postgresql

The Problem
I have the following trigger function that uses the moddatetime extension:
/* BEFORE UPDATE trigger - Updates "entity_version.updated_on", setting it to the current UTC time. */
CREATE EXTENSION IF NOT EXISTS moddatetime; -- Needs superuser privileges!
DROP TRIGGER IF EXISTS trig_entity_version_before_update
ON entity_version;
CREATE TRIGGER trig_entity_version_before_update
BEFORE UPDATE
ON entity_version
FOR EACH ROW EXECUTE PROCEDURE moddatetime(updated_on); -- updated_on is TIMESTAMP type
The trigger works perfectly fine, but the issue is that the first line (CREATE EXTENSION) requires super-user privileges. Since these databases are going to be created by users (via a script) I don't want them the user that makes these databases and triggers to have super user access.
What I've tried
As a first step, running the script as a super user works fine, as you'd expect.
Naturally, the next step would be to separate the creation of the extension as a superuser from the trigger creation script. But doing that, if I run the above script without CREATE EXTENSION line, I get the following error:
function moddatetime() does not exist
Which I suppose makes sense given that the script never declares moddatetime, but I cannot find anywhere in the documentation how to define the extension as available without using CREATE EXTENSION. Surely there must be a way to import or use an extension without having the create it? Something akin to:
USING EXTENSION moddatetime; -- Does something like this exist?
... rest of the trigger script ...

You need the extension installed if you want to use it.
PostgreSQL v13 introduces the notion of a trusted extension, which can be installed by non-superusers with the CREATE privilege on the database, but this extension is not among them. So upgrading won't fix the problem.
You could connect to template1 as superuser and create the extension there. Then it would automatically be present in all newly created databases.
But frankly, I don't see the point. You can do the same thing with a regular PL/pgSQL trigger – only the performance wouldn't be as good.

As per #laurenz-albe his suggestion, I've just stepped off of using the extension for this particular use-case. Fortunately, writing a function to do what the extension does is relatively trivial. I'll include it here for reference in case someone is looking specifically to replace moddatetime.
DROP FUNCTION IF EXISTS fn_entity_version_before_update() CASCADE;
CREATE FUNCTION fn_entity_version_before_update() RETURNS TRIGGER LANGUAGE PLPGSQL AS
$fn_body$
BEGIN
NEW.updated_on = NOW() AT TIME ZONE 'utc';
RETURN NEW;
END;
$fn_body$;
DROP TRIGGER IF EXISTS trig_entity_version_before_update
ON entity_version;
CREATE TRIGGER trig_entity_version_before_update
BEFORE UPDATE
ON entity_version
FOR EACH ROW EXECUTE PROCEDURE fn_entity_version_before_update();

Related

How do I create a temporary trigger in Postgres? [duplicate]

This question already has an answer here:
Drop trigger/function at end of session in PostgreSQL?
(1 answer)
Closed last month.
I'm trying to create a system in Postgres where each client can create its own subscriptions via listen + notify + triggers. I.e. the client will listen to a channel, and then create a trigger which runs notify on that channel when its conditions have been met. The issue is that I want Postgres to clean up properly in case of improper client termination (e.g. the remote client process dies). To be more specific, I want that trigger which is calling the notify to be removed as there is no longer a listener anyways. How can I accomplish this?
I've thought about having a table to map triggers to client ids and then using that to remove triggers where the client is gone, but it seems like a not so great solution.
I found an answer to this in another question: Drop trigger/function at end of session in PostgreSQL?
In reasonably recent Postgres versions you can create a function in
pg_temp schema:
create function pg_temp.get_true() returns boolean language sql as $$ select true; $$;
select pg_temp.get_true();
This is the schema in which temporary tables are created. All its
contents, including your function, will be deleted on end of session.
You can also create triggers using temporary functions on tables. I've
just tested this and it works as expected:
create function pg_temp.ignore_writes() returns trigger language plpgsql as $$
begin
return NULL;
end;
$$;
create table test (id int);
create trigger test_ignore_writes
before insert, update, delete on test
for each row execute procedure pg_temp.ignore_writes();
Because this trigger function always returns NULL and is before [event] it should make any writes to this table to be ignored. And
indeed:
insert into test values(1);
select count(*) from test;
count
-------
0
But after logout and login this function and the trigger would not be
present anymore, so writes would work:
insert into test values(1);
select count(*) from test;
count
-------
1
But you should be aware that this is somewhat hackish — not often used
and might not be very thoroughly tested.
That's not how it works. CREATE TRIGGER requires that you either own the table or have the TRIGGER privilege on it (which nobody in their right mind will give you, because it enables you to run arbitrary code in their name). Moreover, CREATE TRIGGER requires an SHARE ROW EXCLUSIVE lock on the table and DROP TRIGGER requires an ACCESS EXCLUSIVE lock, which can be disruptive.
Create a single trigger and keep that around.

How to create a trigger on a postgresql database managed by prisma?

Good evening . I'm working on a chat application using nodejs, prisma, postgresql. I would like 24 hours after the last message is created on a specific chat to be immediately deleted from the postgresql database. For this, I created a trigger:
--function creation
create or replace function chat_expired() returns trigger as $delete_account$
begin
delete from chat where current_timestamp - last.messages.createAt = 24*60*60;
return new;
end;
$delete_account$ language plpgsql ;
-- trigger creation
create trigger messages_added
after update
on chat --I get error on this line
for each row
execute procedure chat_expired();
from postgresql query tool but when I execute I get the error the cat relationship does not exist at the trigger definition level. I guess it's because the chat table hasn't been defined in my query tool space; but I didn't set it because the chat table was already created from prisma immigrations. So I deleted the folder of my prisma migrations, then I created a new migration and I modified the .sql file manually in order to insert my trigger there thus thinking that prisma would also create a trigger for me at the same time as database tables:
--function creation
create or replace function "chat_expired"() returns trigger as $delete_account$
begin
delete from "Chat" where current_timestamp - last.chatMessage.createdAt = 3*60;
return new;
end;
$delete_account$ language plpgsql ;
-- trigger creation
create trigger "messages_added"
after update
on "chat"
for each row
execute procedure "chat_expired"();
Notes: The previous code contains a few more quotes than the first code. But it instead prevented the migration from applying and I got the error
Error: P3006
Migration `20220905231003_initial_migration` failed to apply cleanly to the shadow database.
Error code: P1014
Error:
The underlying table for model `(not available)` does not exist.
which was only resolved after I removed the manually added rows. So I don't know how to create a trigger on a postgresql database managed by prisma. I've been thinking about the solution for several weeks. I even looked at this stackoverflow question and also this one but nothing. Thanks !
Prisma doesn't have support for triggers yet. Prisma has an excellent article on the topic. They advice writing Prisma middleware to achieve "triggers". There is also a feature request for it.

Postgres 10 - Targeting schema's function on a multiple schema Database

I'm running PostgresSQL 10, and I have several schemas on my DB with multiple functions. I've created a schemaless script with all the functions on it (I've removed the schema prefix), with this, everytime i create a new schema, I ran the migration and create all the functions as well.
This was necessary/requested for a better data separation between customers. All the schemas are twins in terms of structure.
All was fine until I figured that SchemaA was calling a function from public. Even if I call:
SchemaA.myFunction(p_param1:= 'A', p_param2:= 'B').
If this "myFunction" calls another from the inside, it will target public schema by default.
The only way I made it work, was using an input parameter called p_user_schema myFunction(p_param1, p_param2, p_user_schema) and add the following statement as the first line of myFunction body.
EXECUTE FORMAT('SET search_path TO %L', p_user_schema);
I've 147 functions, I will need to adapt each of these, does anyone know a better way to target the callers schema, by callers I mean the prefix schema used on the main call.
You can set the search path at the function level with the current user as the 1st one
CREATE OR REPLACE FUNCTION schemaA.myfunction()
RETURNS ..
AS $$
...
$$ LANGUAGE SQL
SET SEARCH_PATH = "$user", public;

Create Triggers on SYSUSER table in sql Anywhere 16

I want to call a certain procedure that logs everytime a database user is created or deleted in the db ( sql Anywhere 16).
For this i have written a Function that should be called via a trigger when a row is inserted or deleted from table SYS.SYSUSER.
However, I am not able to create a trigger on this table.
Am i allowed to create trigger on this or is there someother way to get notified whenever a user is created or deleted for db?
New to sybase please help.
heres is my create trigger code
CREATE TRIGGER myTrigger AFTER INSERT ON sys.sysuser
REFERENCING NEW AS newRecord
FOR EACH ROW
BEGIN
--
END;
You cannot create a trigger on a system table. You can create a handler for "system events", which are described in the online SQL Anywhere documentation. Unfortunately, creation or deletion of users is not a system event that can be handled. I don't believe there's a way short of polling that you can do what you want to do.
Full disclosure: I work for SAP in SQL Anywhere engineering.

execute a trigger when I create a table

I would like to know if a trigger on a system table of PostgreSQL can be executed when I create a table
I need to add 2 functions on each table of my database and I would like to do it dynamically
Thanks
This can be done with an event trigger:
CREATE OR REPLACE FUNCTION on_create_table_func()
RETURNS event_trigger AS $$
BEGIN
-- your code here
END
$$
LANGUAGE plpgsql;
CREATE EVENT TRIGGER
on_create_table ON ddl_command_end
WHEN TAG IN ('CREATE TABLE')
EXECUTE PROCEDURE on_create_table_func();
Note that there is no way to directly execute any query on the newly created table, or even get its name.
I don't know what you mean by "add 2 functions on each table" since functions don't belong to a specific table, but if you need to perform an operation specific for the new tables, this might not be for you.
I know it's an old question but now it has been implemented in version 9.3, or at least partially
http://www.postgresql.org/docs/9.3/static/event-trigger-definition.html
You're looking for "DDL Triggers". They're not implemented in PostgreSQL. Neither you can add triggers to system tables. Look at this forum entry:
Adding ddl audit trigger