I am learning triggers in PostgreSQL.
I have created a trigger function update_name():
CREATE OR REPLACE FUNCTION update_name()
RETURNS trigger AS
$BODY$
BEGIN
NEW.name := "ankit";
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION update_name()
OWNER TO postgres;
My table user_table is:
CREATE TABLE user_table (
name character varying(20) NOT NULL,
password character varying(20),
email character varying(20),
gender character varying(20),
phone bigint,
CONSTRAINT user_table_pkey PRIMARY KEY (name)
);
and the trigger for the table is :
CREATE TRIGGER "change-name"
BEFORE INSERT OR UPDATE
ON user_table
FOR EACH ROW
EXECUTE PROCEDURE update_name();
when I am inserting data to my table using query:
INSERT INTO user_table(name, password, email, gender, phone)
VALUES ('aa', '9874', 'poi#ka.in', 'male', 8978987896);
I've got the error:
ERROR: column "ankit" does not exist
LINE 1: SELECT "ankit"
^
QUERY: SELECT "ankit"
CONTEXT: PL/pgSQL function update_name() line 3 at assignment
********** Error **********
ERROR: column "ankit" does not exist
SQL state: 42703
Context: PL/pgSQL function update_name() line 3 at assignment
What am I doing wrong?
This is about PostgreSQL syntax. I removed irrelevant references to pgAdmin from the question.
In Postgres, literal values (constants) are enclosed in single quotes: 'value'.
Double quotes are reserved for identifiers but optional as long as it consists of legal, lower-case letters: "Odd Name" vs. odd_name.
This is also the SQL standard. Start by reading the chapter "Lexical Structure" in the manual.
The ERROR has gone by changing just:
NEW.name := "ankit";
to
NEW.name := 'ankit';
Why is it so???
CREATE OR REPLACE FUNCTION update_name()
RETURNS trigger AS
$BODY$
BEGIN
RAISE NOTICE 'RECORD IS INSERTED %',NEW.name;
INSERT INTO user_table VALUES("ankit",NEW.password,NEW.email,NEW.gender,NEW.phone);
RETURN NEW;
end if;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION update_name()
OWNER TO postgres;
Try this out this will help you.
Related
I want to create the following trigger function in my Postgresql DB :
CREATE FUNCTION attribute_edit_history()
RETURNS TRIGGER AS
$BODY$
BEGIN
Select
CASE
WHEN NOT EXISTS
(SELECT * FROM public."TB02_MDD_KEY" where "ENCODED_ID" =ENCODE(CONVERT_TO(NEW."ATTRIBUTE_NAME", 'UTF-8'), 'base64'))
THEN
CASE
WHEN OLD."ATTRIBUTE_NAME" is distinct from NEW."ATTRIBUTE_NAME"
THEN
INSERT INTO public."TB08_ATTRIBUTE_EDIT_HISTORY"(
"ENCODED_ID_OLD","ENCODED_ID_NEW" , "VERSION", "ATTRIBUTE_OLD", "ATTRIBUTE_NEW", "ATTRIBUTE_NEW_ID")
VALUES ( OLD."ENCODED_ID", NEW."ENCODED_ID", NEW."VERSION", OLD."ATTRIBUTE_NAME", NEW."ATTRIBUTE_NAME", ENCODE(CONVERT_TO(NEW."ATTRIBUTE_NAME", 'UTF-8'), 'base64'));
END;
END;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;
CREATE TRIGGER "attribute_edit_history" BEFORE UPDATE ON "TB02_MDD_KEY"
FOR EACH ROW EXECUTE PROCEDURE attribute_edit_history();
I am getting the following syntax error :
ERROR: syntax error at or near "INTO"
LINE 13: INSERT INTO public."TB08_ATTRIBUTE_EDIT_HISTORY"(
^
SQL state: 42601
Character: 352
I dont know where I am going wrong.
Thanks in advance!
Most likely, you want a simple IF statement rather than nested CASE expressions:
CREATE FUNCTION attribute_edit_history()
RETURNS TRIGGER AS
$BODY$
BEGIN
IF
NOT EXISTS(
SELECT 1
FROM public.TB02_MDD_KEY
WHERE ENCODED_ID = ENCODE(CONVERT_TO(NEW.ATTRIBUTE_NAME, 'UTF-8'), 'base64')
)
AND OLD.ATTRIBUTE_NAME IS DISTINCT FROM NEW.ATTRIBUTE_NAME
THEN
INSERT INTO public.TB08_ATTRIBUTE_EDIT_HISTORY(
ENCODED_ID_OLD,
ENCODED_ID_NEW ,
VERSION,
ATTRIBUTE_OLD,
ATTRIBUTE_NEW,
ATTRIBUTE_NEW_ID
) VALUES (
OLD.ENCODED_ID,
NEW.ENCODED_ID,
NEW.VERSION,
OLD.ATTRIBUTE_NAME,
NEW.ATTRIBUTE_NAME,
ENCODE(CONVERT_TO(NEW.ATTRIBUTE_NAME, 'UTF-8'), 'base64')
);
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;
Note that I removed the double quotes around the tables and columns identifiers; you don't need those (unless you have case-sensitive identifiers, which does not seem to be the case here).
After upgrading from Postgres 9.5 to 11, I am trying to replace the following FOR EACH ROW trigger with a FOR EACH STATEMENT trigger which I hope would be more efficient for my specific use-case:
CREATE OR REPLACE FUNCTION audit_update_operations()
RETURNS TRIGGER
AS
$$
DECLARE
audit_user UUID;
data_before TEXT;
data_after TEXT;
BEGIN
audit_user := coalesce(current_setting('audit.AUDIT_USER', TRUE), '77777777-0000-7777-0000-777777777777')::UUID;
data_before := ROW (old.*);
data_after := ROW (new.*);
INSERT INTO dml_audit_log
(changed_at,
user_id,
operation,
table_name,
data_after,
data_before)
VALUES (now(),
audit_user,
'U',
tg_table_name::TEXT,
data_after,
data_before);
RETURN new;
END ;
$$
LANGUAGE plpgsql;
I thought this was pretty easy:
CREATE OR REPLACE FUNCTION audit_update_operations()
RETURNS TRIGGER
AS
$$
DECLARE
user_id UUID;
BEGIN
user_id := coalesce(current_setting('audit.AUDIT_USER', TRUE), '77777777-0000-7777-0000-777777777777')::UUID;
INSERT INTO dml_audit_log
SELECT now() AS changed_at,
user_id,
'U' AS operation,
tg_table_name::TEXT AS table_name,
ROW (new_table.*) AS data_after,
ROW (old_table.*) AS data_before
FROM new_table;
RETURN NULL;
END ;
$$
LANGUAGE plpgsql;
which is called generically for every table via the following trigger:
EXECUTE ('
CREATE TRIGGER trigger_audit_update
AFTER UPDATE ON ' || tablename || '
REFERENCING
OLD TABLE AS old_table
NEW TABLE AS new_table
FOR EACH STATEMENT
EXECUTE PROCEDURE audit_update_operations()');
but an UPDATE statement triggering this function results in the following error:
[42P01] ERROR: missing FROM-clause entry for table "old_table" Where: PL/pgSQL function shared.audit_update_operations() line 7 at SQL statement
The question I am struggling with is:
How can I correlate the old and new rows without making any assumption about the table being changed?
The tables I am triggering on may or may not have a primary key. Even if they do, I would not know its name or column(s) in the trigger function.
Are the rows in OLD TABLE and NEW TABLE guaranteed to be in the same order? I cannot rely on undocumented implementation details that may change.
Function defined:
CREATE OR REPLACE FUNCTION len_chars(t_name VARCHAR, f_name VARCHAR) RETURNS BIGINT AS $$
BEGIN
SELECT sum(char_length(f_name)) FROM t_name;
END;
$$ LANGUAGE plpgsql;
Calling it from psql
SELECT len_chars('public.tag', 'name');
for table "tag" and column "name" returns Error:
psql: ERROR: relation "t_name" does not exist
LINE 1: SELECT sum(char_length(f_name)) FROM t_name
^
QUERY: SELECT sum(char_length(f_name)) FROM t_name
CONTEXT: PL/pgSQL function len_chars(character varying,character varying) line 1 at SQL statement
Is it possible to choice table name in postgresql functions?
You need dynamic SQL for that. And to safely construct a dynamic SQL string, the best way is to use the format() function using %I placeholders to properly deal with quoting identifiers if needed.
CREATE OR REPLACE FUNCTION len_chars(t_name VARCHAR, f_name VARCHAR)
RETURNS BIGINT AS $$
declare
l_result bigint;
BEGIN
execute format('SELECT sum(char_length(%I)) FROM %I', f_name, t_name)
into l_result;
return l_result;
END;
$$ LANGUAGE plpgsql;
I've tried to encrypt a password in a PostgreSQL database by using a trigger but I can't get it to work, and I don't know where my mistake is.
Here is the code:
CREATE TABLE mms_user (
uid serial,
mail text NOT NULL,
passwd text NOT NULL,
usertype integer NOT NULL,
PRIMARY KEY (uid)
);
CREATE FUNCTION encrypt_password() RETURNS TRIGGER AS $$
BEGIN
NEW.passwd = digest(NEW.passwd, 'sha1');
RETURN NEW; END; $$LANGUAGE 'plpgsql';
CREATE TRIGGER encrypt_userdata AFTER INSERT ON mms_user EXECUTE PROCEDURE encrypt_password();
INSERT INTO mms_user values (default, 'who', 'me', 1);
It says this when executing:
ERROR: record "new" is not assigned yet
DETAIL: The tuple structure of a not-yet-assigned record is indeterminate.
CONTEXT: PL/pgSQL function encrypt_password() line 3 at assignment
How do I access the record I'm inserting if it's not with NEW?
Security and cryptographic considerations aside (see comments), to address your actual PL/pgSQL question:
The trigger function is basically ok. Some cleanup:
CREATE FUNCTION encrypt_password()
RETURNS TRIGGER AS
$func$
BEGIN
NEW.passwd := digest(NEW.passwd, 'sha1'); -- or some other function?
RETURN NEW;
END
$func$ LANGUAGE plpgsql; -- don't quote the language name
But to make it work, you have to make the trigger BEFORE INSERT:
CREATE TRIGGER encrypt_userdata
BEFORE INSERT ON mms_user
EXECUTE PROCEDURE encrypt_password();
I suggest the manual here and here.
I was created a trigger on my table.When insert/update/delete its adds a key i,u,d like that.
And i'm trying to insert this keys on another table but gives me error like that:
ERROR: column "i" does not exist
LINE 1: ...(operation,stamp,userid,empname,salary) VALUES('||i||', now(...
^
QUERY: SELECT dblink_exec('INSERT INTO emp_audit(operation,stamp,userid,empname,salary) VALUES('||i||', now(), user,NEW.*)')
CONTEXT: PL/pgSQL function process_emp_audit() line 14 at PERFORM
********** Error **********
ERROR: column "i" does not exist
SQL state: 42703
Context: PL/pgSQL function process_emp_audit() line 14 at PERFORM
i don't know why it's give me this error.And this is my trigger function
-- Function: process_emp_audit()
-- DROP FUNCTION process_emp_audit();
CREATE OR REPLACE FUNCTION process_emp_audit()
RETURNS trigger AS
$BODY$
BEGIN
PERFORM dblink_connect('dbname=ekders port=5432 user=****** password=*****');
IF (TG_OP = 'DELETE') THEN
PERFORM dblink_exec('INSERT INTO emp_audit "D", now(), user, OLD.*;');
PERFORM dblink_disconnect();
RETURN OLD;
ELSIF (TG_OP = 'UPDATE') THEN
PERFORM dblink_exec('INSERT INTO emp_audit "U", now(), user, NEW.*;');
PERFORM dblink_disconnect();
RETURN NEW;
ELSIF (TG_OP = 'INSERT') THEN
PERFORM dblink_exec('INSERT INTO emp_audit(operation,stamp,userid,empname,salary) VALUES('||i||', now(), user,NEW.*)');
PERFORM dblink_disconnect();
RETURN NEW;
END IF;
RETURN NULL; -- result is ignored since this is an AFTER trigger
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION process_emp_audit()
OWNER TO postgres;
Your code is attempting to concatenate a value i, which doesn't exist, to the string. I suspect what you really wanted to do was escape the single quotes. The below should work:
PERFORM dblink_exec('INSERT INTO emp_audit(operation,stamp,userid,empname,salary) VALUES(''i'', now(), user,NEW.*)');