Creating a trigger gives "function does not exist" error in PostgreSQL - postgresql

I'm working on a project right now where I'm trying to build a trigger that updates a column "lastedit" (in the table Person), which is a timestamp storing when the last change was made to the table in the schema specified in the trigger creation (in this case Certifications).
Now, my problem is that when I try to create the trigger, after creating the function "update_lastedit()", it gives me an error saying that the function does not exist. I think I might have a mismatch in my function somewhere, but I cannot seem to find it.
Could any of you help me out? I'm running PostgreSQL 9.5.5. Please let me know if I need to give a more extensive explanation, this is my first question, so I might have overlooked something important.
My code for the trigger and the function are as follows:
CREATE OR REPLACE FUNCTION update_lastedit() RETURNS TRIGGER AS
$update_edit$
BEGIN
UPDATE ovsoftware.person
SET lastedit = now();
END;
$update_edit$
LANGUAGE plpgsql;
and
CREATE TRIGGER cert_edit_trigger
BEFORE INSERT OR UPDATE ON ovsoftware.certifications
FOR EACH ROW
EXECUTE PROCEDURE update_lastedit();
The exact error:
SQL fout:
ERROR: function update_lastedit() does not exist
In statement:
CREATE TRIGGER cert_edit_trigger
BEFORE INSERT OR UPDATE ON ovsoftware.certifications
FOR EACH ROW
EXECUTE PROCEDURE update_lastedit();

The solution was to use a fully qualified name for the function as follows: ovsoftware.update_lastedit(). I am not sure why that is the case, as I did not need to do so in other cases.
Either way, the scope apparently did not include the ovsoftware schema, leading to the error.

Related

Another missing FROM-clause entry for table

I'm new to postgresql and I have this problem with trigger, that should just take value from the sequence. If I understood correctly, (tried few examples) in case like this you don't need clause FROM (like in ORACLE you would add 'from dual').
Trigger looks something like that:
CREATE OR REPLACE FUNCTION public.trigger_fct_name_id_autoinc()
RETURNS trigger AS
$$
BEGIN
SELECT "NAME_ID_SEQ".NEXTVAL INTO NEW."ID" ;
RETURN NEW;
END
$$
LANGUAGE 'plpgsql';
Of course, I've done something wrong. Error I get is:
ERROR: missing FROM-clause entry for table "NAME_ID_SEQ" Where: PL/pgSQL function trigger_fct_name_id_autoinc() line 3 at SQL statement
Tried to find some answers in the web, but either there was none, or I don't understand postgresql well enough to understand what people write. In most topics the answer involves aliases, but I don't think that's the case here.
Should I somehow add FROM clause, or is it just a sign of a different issue?
You are using Oracle syntax to get the next value from your sequence. Use the Postgres' nextval:
SELECT nextval('NAME_ID_SEQ') INTO NEW."ID" ;

Sanitize input to a column in postgres

So, I think this should be fairly simple, but the documentation makes it seem somewhat more complicated. I've written an SQL function in PostgreSQL (8.1, for now) which does some cleanup on some string input. For what it's worth, the string is an LDAP distinguished name, and I want there to consistently be no spaces after the commas - and the function is clean_dn(), which returns the cleaned DN. I want to do the same thing to force all input to another couple of columns to lower case, etc - which should be easy once I figure this part out.
Anyway, I want this function to be run on the "dn" column of a table any time anyone attempts to insert to or update and modify that column. But all the rule examples I can find seem to make the assumption that all insert/update queries modify all the columns in a table all the time. In my situation, that is not the case. What I think I really want is a constraint which just changes the value rather than returning true or false, but that doesn't seem to make sense with the SQL idea of a constraint. Do I have my rule do an UPDATE into the NEW table? Do I have to create a new rule for every possible combination of NEW values? And if I add a column, do I have to go through and update all of my rule combinations to refelect every possible new combination of columns?
There has to be an easy way...
First, update to a current version of PostgreSQL. 8.1 is long dead and forgotten und unsupported and very, very old .. you get my point? Current version is PostgreSQL 9.2.
Then, use a trigger instead of a rule. It's simpler. It's the way most people go. I do.
For column col in table tbl ...
First, create a trigger function:
CREATE OR REPLACE FUNCTION trg_tbl_insupbef()
RETURNS trigger AS
$BODY$
BEGIN
NEW.col := f_myfunc(NEW.col); -- your function here, must return matching type
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
Then use it in a trigger.
For ancient Postgres 8.1:
CREATE TRIGGER insupbef
BEFORE INSERT OR UPDATE
ON tbl
FOR EACH ROW
EXECUTE PROCEDURE trg_tbl_insupbef();
For modern day Postgres (9.0+)
CREATE TRIGGER insbef
BEFORE INSERT OR UPDATE OF col -- only call trigger, if column was updated
ON tbl
FOR EACH ROW
EXECUTE PROCEDURE trg_tbl_insupbef();
You could pack more stuff into one trigger, but then you can't condition the UPDATE trigger on just the one column ...

Execute triggers function of another schema on the actual chema

my problem is easy to explain with an example: I have a 'common' schema (the public one?) where I store common data between a clustered application.
For every instance of my application, I have a role (used as the application user).
And i have a common role, app_users, with read-only privileges on the common schema, and every application role is a member of app_users.
Now my problem is: how can i set a trigger on the app_a scheme that execute a function (procedure) in the common scheme, but affect the (and only the) app_a tables?
I mean:
// common_scheme, dummy function to emulate the mysql on update = now()
CREATE OR REPLACEFUNCTION update_etime() RETURNS TRIGGER AS $$
BEGIN
NEW.etime = date_part('epoch'::text, now())::int;
RETURN NEW;
END;
$$ language plpgsql;
// now, in the app_foo scheme, i have the table:
CREATE TABLE foo_table (fid serial not null primary key unique, label char(25));
// and the trigger:
CREATE TRIGGER foo_table_update_etime BEFORE UPDATE ON foo_talbe FOR EACH ROW EXECUTE PROCEDURE update_etime();
// ERROR: function update_etime() does not exist
CREATE TRIGGER foo_table_update_etime BEFORE UPDATE ON foo_talbe FOR EACH ROW EXECUTE PROCEDURE common_scheme.update_etime();
// ERROR: function common_scheme.update_etime() does not exist
The user that will access app_foo has the execute privilege on update_etime() function in common_schema.
Any idea?
I've googled around but the only solution I fount to call functions from other schemas is something like execute 'select * from ' || schema_name || '.table_name'; but i dont think this will do the trick in my case, becose the function must work with the 'local' scheme.
Your second set of syntax should work... the one with "EXECUTE PROCEDURE common_scheme.update_etime();"
If it isn't finding the function, I'd guess that you either have created it in a different schema than you think it is in, or you haven't created it at all (and note, your example create syntax has a bug, no space between "replace" and "function", which would cause an error when trying to create the function. Try doing a:
\df *.update_etime
As superuser to verify the function exists and is in the location you think it is in. HTH.

increasing entry number automatically for every insert on the entry table

i tried using this code:
CREATE OR REPLACE FUNCTION eno_inc() RETURNS trigger AS $eno_inc$
BEGIN
NEW.eno := OLD.eno + 1;
END;
$eno_inc$
LANGUAGE plpgsql;
but anytime i run it generates this error:
ERROR: record "old" is not assigned yet
DETAIL: The tuple structure of a not-yet-assigned record is indeterminate.
CONTEXT: PL/pgSQL function "eno_inc" line 4 at assignment
*** Error ***
ERROR: record "old" is not assigned yet
SQL state: 55000
P.S i am very new to SQL
You are missing a
RETURN NEW;
at the end.
The error
ERROR: record "old" is not assigned yet SQL state: 55000
does show that this MIGHT not be a trigger (OLD and NEW are only used in trigger context). You need to define a trigger for that to work. I suggest you read the docs about trigger functions.
As the comment by Steve C notes, make sure you don't just need a SERIAL (auto-increment in MySQL terms).
In addition to what #DrColossos already wrote ..
OLD and NEW are only defined in a trigger ON UPDATE. In a trigger on INSERT you would get this error message. In a trigger on DELETE you only have OLD at your disposal.
Also, for a trigger to work you need a trigger function plus a trigger. You may be missing that, too. Start by reading the excellent manual here.
But I suspect your problems are more basic than that. First of all, as #Steve commented, you are probably just looking for a serial column or a sequence and a DEFAULT to your already existing column.

debugging postgresql trigger

I have this Trigger in Postgresql that I can't just get to work (does nothing). For understanding, there's how I defined it:
CREATE TABLE documents (
...
modification_time timestamp with time zone DEFAULT now()
);
CREATE FUNCTION documents_update_mod_time() RETURNS trigger
AS $$
begin
new.modification_time := now();
return new;
end
$$
LANGUAGE plpgsql;
CREATE TRIGGER documents_modification_time
BEFORE INSERT OR UPDATE ON documents
FOR EACH ROW
EXECUTE PROCEDURE documents_update_mod_time();
Now to make it a bit more interesting.. How do you debug triggers?
Use the following code within a trigger function, then watch the 'messages' tab in pgAdmin3 or the output in psql:
RAISE NOTICE 'myplpgsqlval is currently %', myplpgsqlval; -- either this
RAISE EXCEPTION 'failed'; -- or that
To see which triggers actually get called, how many times etc, the following statement is the life-saver of choice:
EXPLAIN ANALYZE UPDATE table SET foo='bar'; -- shows the called triggers
Note that if your trigger is not getting called and you use inheritance, it may be that you've only defined a trigger on the parent table, whereas triggers are not inherited by child tables automatically.
To step through the function, you can use the debugger built into pgAdmin3, which on Windows is enabled by default; all you have to do is execute the code found in ...\8.3\share\contrib\pldbgapi.sql against the database you're debugging, restart pgAdmin3, right-click your trigger function, hit 'Set Breakpoint', and then execute a statement that would cause the trigger to fire, such as the UPDATE statement above.
Turns out I was using inheritance in the above problem and forgot to mention it. Now for everybody who might run into this as well, here's some debugging hints:
Use the following code to debug what a trigger is doing:
RAISE NOTICE 'test'; -- either this
RAISE EXCEPTION 'failed'; -- or that
To see which triggers actually get called, how many times etc, the following statement is the life-saver of choice:
EXPLAIN ANALYZE UPDATE table SET foo='bar'; -- shows the called triggers
Then there's the one thing I didn't know before: triggers only fire when updating the exact table they're defined on. If you use inheritance, you MUST define them on the child tables as well!
You can use 'raise notice' statements inside your trigger function to debug it. To debug the trigger not being called at all is another story.
If you add a 'raise exception' inside your trigger function, can you still do inserts/updates?
Also, if your update test occurs in the same transaction as your insert test, now() will be the same (since it's only calculated once per transaction) and therefore the update won't seem to do anything. If that's the case, either do them in separate transactions, or if this is a unit test and you can't do that, use clock_timestamp().
I have a unit test that depends on some time going by between transactions, so at the beginning of the unit test I have something like:
ALTER TABLE documents
ALTER COLUMN modification_time SET DEFAULT clock_timestamp();
Then in the trigger, use "set modification_time = default".
So normally it doesn't do the extra calculation, but during a unit test this allows me to do inserts with pg_sleep in between to simulate time passing and actually have that be reflected in the data.