Postgresql: execute update on a temporary table in plpgsql is not working - postgresql

I'm trying to update a field in a temporary table I've created.
The code for the temporary table looks like this:
CREATE OR REPLACE FUNCTION insertTable ()
RETURNS VOID AS $$
BEGIN
execute 'create temporary table myTable (id INTEGER, value TEXT) on commit preserve rows';
execute 'insert into myTable values(1, NULL)';
end if;
end;
$$ LANGUAGE plpgsql;
Next I try to update the value filed with the following function:
CREATE OR REPLACE FUNCTION setValue (msg TEXT)
RETURNS VOID AS $$
BEGIN
EXECUTE format('UPDATE myTable SET value = value || $1 WHERE id = '|| quote_literal(1))USING msg;
END;
$$ LANGUAGE plpgsql;
However it does not work and the value field stays empty.
I tried the same code with an already existing table (not temporary) and the code worked as expected.
I searched the documentation for a difference between updating a temporary and a normal table but couldn't find any.
I hope you can help me with this.
Thanks in advance for your help.
Edit: edited the name of the table

The issue is not related to temporary table. The problem is that the column you want to update is actually empty. You try to update this column by concatenating the value of the column with another text, but, because the value itself is null, the concatenated value is also null.
This query:
SELECT null||'some text';
returns null. Also this update statement:
UPDATE xmlBuffer SET value = value || 'some text';
will not update the rows where the actual content is null. You could fix this issue in several ways, depending on you needs. In example you could use the COALESCE statement in the second function, using a empty string as fallback value (besides, the quote_literal and formatstatements are not necessary):
CREATE OR REPLACE FUNCTION setValue (msg TEXT)
RETURNS VOID AS $$
BEGIN
EXECUTE 'UPDATE xmlBuffer SET value = COALESCE(value,'''') || $1 WHERE id = '|| 1
USING msg;
END;
$$ LANGUAGE plpgsql;

Related

Access data of variable in PL/PgSQL

I have a PL/PgSQL function like this:
CREATE FUNCTION get_value(firstval integer) RETURNS SETOF mytable AS
$func$
DECLARE
current mytable;
BEGIN
SELECT fv FROM mytable WHERE fv = fistval INTO current;
IF current IS NULL THEN
INSERT INTO mytable(fv) VALUES (firstval);
END IF;
RETURN current.fv;
END
$func$ LANGUAGE plpgsql;
I need to return a specific row (fv) of the current variable, but this code is not working (it does not return anything), so how I have to do this?
There are lot of issues:
current is declared like record. But you try to assign integer value to this composite variable SELECT INTO.
The return from table functions should be realised by RETURN TABLE statement. There are not any table with name mytable. In Postgres concept - RETURNS SETOF mytable means - returns rows of type like table "mytable".
The test IS NULL is not safe in this case. You want to check FOUND variable.
Maybe you don't want to return table - then there is badly used SETOF clause.
CREATE OR REPLACE FUNCTION get_value(firstval int)
RETURNS mytable AS $$
DECLARE
_r mytable;
BEGIN
SELECT * FROM mytable WHERE fv = firstval INTO _r;
IF NOT FOUND THEN
INSERT INTO mytable(fv) VALUES(firstval);
SELECT * FROM mytable WHERE fv = firstval INTO _r;
END IF;
RETURN _r;
END;
$$ LANGUAGE plpgsql;
Attention! - this code maybe does what you want, but it is not safe against race conditions. Better to use Postgres statement INSERT ON CONFLICT DO.
Please, try to read documentation first - it is designed differently than you are expecting.

Fill field based on column other table

I have a really simple problem and I am probably overthinking this way too much. But here it goes:
I want the fields of a column in one of my tables to be filled automatically whenever I make a new record. The value should be the same (UUID) as the specified (UUID) value from a column in another table. These two columns are joined via a foreign key. So far I have tried making a trigger function but with no results so far:
Create or replace function project_id()
returns trigger
as $$ begin
if new.project_id is null then
insert into sporen (project_id)
select project_id
from project_info
where project_code = 'ant0001';
end if;
return new;
end;
$$ language plpgsql;
CREATE TRIGGER
project_id_default
BEFORE update ON
sporen
FOR EACH ROW EXECUTE PROCEDURE project_id();
Do I need to specify something as a default in my table? Or am I going about it completely wrong?
You only need to assign project_info.project_id to NEW.project_id in your trigger function. No INSERT is needed. Here is an illustration.
Create or replace function project_id() returns trigger as
$$
begin
if new.project_id is null then
new.project_id :=
(
select pi.project_id
from project_info pi
where pi.project_code = NEW.project_code
);
end if;
return new;
end;
$$ language plpgsql;
You do not need to specify a default value for project_id in your table.

How to create trigger to insert a sequence of numbers in postgresql; but the insert statement validated before ..?

Good morning everyone, I have a question about the following case:
I have a trigger and a function that inserts a land code, but when it works very well when inserting a row.
But when an insert statement fails to execute for any problems in the expression, the sequence function generates a value before inserting the row, losing the order in the numeration.
There is a way to make a change in the trigger or function, to validate me before the INSERT expression before moving to the sequence function and thereby avoid those jumps of numeration.
Deputy code (triger and function) and images of the tables.
CODE:
CREATE TRIGGER trigger_codigo_pech
BEFORE INSERT ON independizacion
FOR EACH ROW
EXECUTE PROCEDURE codigo_pech();
CREATE OR REPLACE FUNCTION codigo_pech()
RETURNS trigger
AS $$
DECLARE
incremento INTEGER;
cod_inde text;
BEGIN
IF (NEW.cod_inde IS NULL OR NEW.cod_inde = '''' ) THEN
incremento = nextval ('codigo_pech');
NEW.cod_inde = 'PECH' || '-' || incremento;
END IF;
RETURN NEW;
END;
$$ LANGUAGE 'plpgsql';
CAPTURE QUERY RESULT
As you can see, it would also be necessary to make a trigger on the primary key to prevent jumps in the numeration.
I hope your help. Thank you
You can make incremento.cod_inde DEFERRABLE and INITIALLY DEFERRED:
ALTER TABLE incremento ALTER COLUMN cod_inde SET DEFAULT 0;
ALTER TABLE incremento
ALTER CONSTRAINT incremento_cod_inde_key
DEFERRABLE INITIALLY DEFERRED;
Then assign the nextval('codigo_pech') in a AFTER INSERT trigger:
CREATE OR REPLACE FUNCTION codigo_pech_after() RETURNS trigger AS $$
BEGIN
UPDATE incremento SET
cod_inde = 'PECH-' || (nextval('codigo_pech'))::text
WHERE id = NEW.id; -- replace id with your table's primary key
END;
$$ LANGUAGE plpgsql;

ERROR: unterminated dollar-quoted string at or near "$BODY$

I'm using sql fiddle...PostgreSQL 9.3:
CREATE TABLE HotelStays
(roomNum INTEGER NOT NULL,
arrDate DATE NOT NULL,
depDate DATE NOT NULL,
guestName CHAR(30) NOT NULL,
PRIMARY KEY (roomNum, arrDate))
;
CREATE OR REPLACE FUNCTION new_customer() RETURNS void AS
$BODY$
DECLARE
depatureDate DATE;\
BEGIN
SELECT depDate INTO depatureDate FROM HotelStays WHERE OLD.roomNum = NEW.roomNum;
IF (depatureDate <= NEW,arrDate)
INSERT INTO HotelStays (roomNum, arrDate, depDate, guestName)
VALUES (:NEW.roomNum, :NEW.arrDate, :NEW.depDate, :NEW.guestName);
END IF;
RETURN;
END
$BODY$
LANGUAGE 'plpgsql' ;
CREATE TRIGGER;
INSERT INTO HotelStays(roomNum, arrDate, depDate, guestName)
VALUES
(123, to_date('20160202', 'YYYYMMDD'), to_date('20160206','YYYYMMDD'), 'A');
Problem I am trying to solve: a new entry (for a new guest) could be put in for a room number, even before the existing guest has checked out.
I'm trying to solve this question using triggers. Please help me out. Thanks in advance.
As author mentioned, he used SQL Fiddle. I had the same problem with db-fiddle.com and resolved it by replacing $$ or $BODY$ with single quotes ' (and doubling single quotes elsewhere in between.
For example, db-fiddle
CREATE OR REPLACE FUNCTION update_datem()
RETURNS trigger AS
'
BEGIN
NEW.dateM = DATE_TRUNC(''MONTH'', NEW.date);
RETURN NEW;
END;
'
LANGUAGE plpgsql;
There are several errors in your code. First the backslash in depatureDate DATE;\. You are also missing a THEN for the IF clause and new does not need a : in front of it. You also have a , instead of a . in NEW,arrDate. And the final END is missing a ;.
Not an error, but the language name is an identifier, do not put it in single quotes.
The line CREATE TRIGGER; is also wrong. If you want to create trigger your function also needs to be declared as returns trigger and has to return the new row if it is a "before" trigger. If you intend to use an after trigger you still need to return something from that.
I am not sure what the condition WHERE OLD.roomNum = NEW.roomNum; is supposed to select. If you want to get the room number of the changed row, just use new.depdate. The select .. into ... will fail if that query returns more then one row. You probably meant to use where roomnum = new.roomnum or something similar.
So the function should be something like this:
CREATE OR REPLACE FUNCTION new_customer()
RETURNS trigger
AS
$BODY$
DECLARE
depatureDate DATE;
BEGIN
SELECT depDate
INTO depatureDate
FROM HotelStays
WHERE roomNum = NEW.roomNum;
IF (depatureDate <= NEW.arrDate) THEN
INSERT INTO HotelStays (roomNum, arrDate, depDate, guestName)
VALUES (NEW.roomNum, nEW.arrDate, NEW.depDate, NEW.guestName);
END IF;
RETURN NEW; -- this is important for a trigger
END;
$BODY$
LANGUAGE plpgsql;
And the code to create the trigger would be something like this:
CREATE TRIGGER check_stays
before update or insert on hotelstays
execute procedure new_customer();

Function to DELETE rows taking the tablename as parameter

I want create a function:
CREATE OR REPLACE FUNCTION medibv.delAuto(tableName nvarchar(50), columnName nvarchar(100),value
nvarchar(100))
RETURNS void AS
$BODY$
begin
DELETE from tableName where columnName=value
end;
$BODY$
LANGUAGE plpgsql VOLATILE;
I have these parameters: tableName, columnName, value.
I want tableName as table in PostgreSQL.
CREATE OR REPLACE FUNCTION medibv.delauto(tbl regclass, col text, val text
,OUT success bool)
RETURNS bool AS
$func$
BEGIN
EXECUTE format('
DELETE FROM %s
WHERE %I = $1
RETURNING TRUE', tbl, col)
USING val
INTO success;
RETURN; -- optional in this case
END
$func$ LANGUAGE plpgsql;
Call:
SELECT medibv.delauto('myschema.mytbl', 'my_txt_col', 'foo');
Returns TRUE or NULL.
There is no nvarchar type in Postgres. You may be thinking of SQL Server. The equivalent would be varchar, but most of the time you can simply use text.
regclass is a specialized type for registered table names. It's perfect for the case an prevents SQL injection for the table name automatically and most effectively. More in the related answer below.
The column name is still prone to SQL injection. I sanitize the function with format(%I).
format() requires PostgreSQL 9.1+.
Your function did not report what happened. One or more rows may be found and deleted. Or none at all. As a bare minimum I added a boolean OUT column which will be TRUE if one or more rows were deleted. Because (quoting the manual here):
If multiple rows are returned, only the first will be assigned to the INTO variable.
Lastly, use USING with EXECUTE to pass in values. Don't cast back and forth. This is inefficient and prone to errors and to SQLi once more.
Find more explanation and links in this closely related answer:
Table name as a PostgreSQL function parameter
Use EXECUTE to run dynamic commands:
CREATE OR REPLACE FUNCTION medibv.delAuto(tableName nvarchar(50), columnName nvarchar(100),value
nvarchar(100))
RETURNS void AS
$BODY$
begin
EXECUTE 'DELETE FROM ' || tableName || ' WHERE ' || columnName || '=' || value;
end;
$BODY$
LANGUAGE plpgsql VOLATILE;