Trigger to create record from JSON object in PostgreSQL - json

Concept: a trigger what creates a new record in a table after a new JSON object has been created in another table. I don't want to make any modifications yet, just to "convert" JSON objects to records with a trigger.

Use the function jsonb_populate_record() in the trigger function, e.g.
create or replace function json_input_trigger()
returns trigger language plpgsql as $$
begin
insert into main_table
select *
from jsonb_populate_record(null::main_table, new.data);
return new;
end $$;
Fully working example.

Related

Need Assistance with a Postgres Trigger and Function

I have a lookup table that holds a column of sources (from various hardcoded campaigns captured through a webservice API I created) and the respective brands that should be associated with them. This is so I can give a brand to records where brand is null - so that they can be welcomed with a certain template through a marketing automation tool.
I'm eventually deprecating this API and replacing it with one where brand is required, but in the meantime I have to craft a temporary solution until I give all my brand teams time to change their API calls.
I wrote this function:
CREATE OR REPLACE FUNCTION public.brand_lookup(IN i_brand TEXT )
RETURNS SETOF RECORD VOLATILE AS
$$
BEGIN
RETURN QUERY
UPDATE subscriber
SET brand = (SELECT brand FROM brand_translation
WHERE source = subscriber.source);
END;
$$
LANGUAGE plpgsql;
And a trigger to fire the function when a record is inserted:
CREATE TRIGGER brand_translation
AFTER INSERT ON subscriber
FOR EACH ROW EXECUTE PROCEDURE public.brand_lookup();
But my trigger comes back with an error that "ERROR: function public.brand_lookup() does not exist" (but it created successfully)". Besides the fact that my trigger doesn't see my function, will that function do what I'm intending? I'm fairly noob at functions (as you can probably tell).
It might work like this:
CREATE OR REPLACE FUNCTION public.f_brand_lookup()
RETURNS trigger AS
$func$
BEGIN
SELECT INTO NEW.brand
bt.brand
FROM brand_translation bt
WHERE bt.source = NEW.source;
RETURN NEW;
END
$func$
LANGUAGE plpgsql;
CREATE TRIGGER brand_insert_before_lookup
BEFORE INSERT ON subscriber
FOR EACH ROW EXECUTE PROCEDURE public.f_brand_lookup();
There is just too much completely wrong with your example.
You need to start by studying the basics. As always, I suggest the very fine manual.
Start here and here.
Postres allows for overloading of functions depending on the input parameters-- so when you call public.brand_lookup() with no parameters, the function you created-- public.brand_lookup(text)-- is not found. I think you need to pass the necessary parameter in the call to the function, or remove the parameter in the function definition.

Is it possible to pass the NEW and the OLD tables from a trigger into a procedure in MySQL?

Is it possible to pass the NEW and the OLD tables from a trigger into a procedure in MySQL?
I suspect no, since there is no such a datatype as table that a procedure accepts.
Any workarounds possible?
Ideally it would look like this:
CREATE TRIGGER Product_log AFTER UPDATE ON Product
FOR EACH ROW BEGIN
call logChanges(OLD, NEW);
END;
You can explicitly pass each field:
CALL logChanges(OLD.colA, OLD.colB, NEW.colA, NEW.colB);
Or if logChanges must be sufficiently generic that it can handle such calls from different tables, one could concatenate the field values into a single string using a suitable delimiter (e.g. the unit separator):
CALL logChanges(CONCAT_WS(CHAR(31), OLD.colA, old.colB),
CONCAT_WS(CHAR(31), NEW.colA, NEW.colB));
Or if data types must be preserved, one could insert the records into a temporary from which logChanges reads.
it's not possible because there is no NEW or OLD table. The entire trigger is related to the table - the "new" and "old" refer to the rows and the values they contained before and after the event that was triggered. In other words, your example would be:
call logChanges(OLD.customername, NEW.customername)
You could also save all the OLD data in a history table (which I expect logchanges does anyways), basically being a clone of the production table something like this:
BEGIN
IF OLD.customer_name != NEW.customer_name
THEN
INSERT INTO myTable_chagne_history
(
customer_id ,
customer_name ,
another_field ,
edit_time
)
VALUES
(
OLD.customer_id,
OLD.customer_name,
OLD.another_field ,
NEW.time_edit_was_made
);
END IF;
END;

PostgreSQL functions and triggers

I am trying out functions and triggers int postgreSQL, however i am having a problem, when the function is triggered it is giving me an error
ERROR: control reached end of trigger procedure without RETURN
this particular procedure is only executing an insert into command so i do not see why it needs a return
this is the script:
CREATE OR REPLACE FUNCTION forest_aud_func() returns trigger as $tree_stamp$
BEGIN
insert into Audit values('k',124,'l');
END;
$tree_stamp$
LANGUAGE plpgsql;
create trigger forest_aud_ins after insert on forest
for each row execute procedure forest_aud_func()
insert into forest values('Blue',1600,'Malta','Health Ltd')
The error message tells you all. You need to do a RETURN from the trigger function:
CREATE OR REPLACE FUNCTION forest_aud_func() returns trigger as $tree_stamp$
BEGIN
insert into Audit values('k',124,'l');
return new;
END;
$tree_stamp$
LANGUAGE plpgsql;
From the manual:
A trigger function must return either NULL or a record/row value having exactly the structure of the table the trigger was fired for.

Suppose I want to create a "stored function" for MYSQL. Where do I wrote it? Which file?

Of course, I could go into mysql console and type the Function. But what if I want to store it for future use? What do I do?
Most projects have an SQL file to initialize the database from scratch. This way, one can set up the application database by simply running this SQL file. Your CREATE PROCEDURE/FUNCTION query would also go in this file.
There's a good tutorial here:
http://www.databasejournal.com/features/mysql/article.php/3525581/MySQL-Stored-Procedures-Part-1.htm
You need to use stored procedures. Once written, these are stored in the database along with your tables. They can be invoked using the CALL <procedure> statement.
Here's an example procedure that populates table1 with random values:
DELIMITER //
DROP PROCEDURE IF EXISTS autofill//
CREATE PROCEDURE autofill()
BEGIN
DECLARE i INT DEFAULT 0;
WHILE i < 20000 DO
INSERT INTO table1 (size) VALUES (FLOOR((RAND() * 1000)));
SET i = i + 1;
END WHILE;
END;
//
DELIMITER ;
Once the procedure has been written, it is called like this:
CALL autofill();

MySQL UpdateXML with automatic node inserting?

I am writing an application that supports custom fields. Currently I store all custom fields in a XML formated text field ( e.g. '<root><field1>val1</field1><field2>val2</field2></root>' in cust_field)
I am able to to use updateXML(cust_field, '/root/field1', '<field1>new value</field1') to update those values, however if I use updateXML(cust_field, '/root/field3', '<field3>new value</field3>') then it does not work since field3 is not in the old value. Is there a way to let MySQL automatically insert the new field3 node and its value into cust_field? I am thinking about stored procedure or even stored function but not familiar with both, can anyone point me to the right direction?
The MySQL XML Functions will not do this automatically.
You can create a stored function to call UpdateXML() if the element is present, otherwise to add the element using your own logic.
Here's a basic template to get you started:
DELIMITER $$
CREATE FUNCTION update_xml(xml_target text, xpath_expr text, new_xml text) returns text
BEGIN
DECLARE return_val text;
IF (ExtractValue(xml_target,xpath_expr) != '')
THEN
RETURN updateXML(xml_target,xpath_expr,new_xml);
ELSE
SET return_val := xml_target;
-- add code here to insert the new element into your XML string
RETURN return_val;
END IF;
END $$
I actually ended up putting the logic in my app sql engine.