So I'm kind of new to databases in general, or at least not very experienced.
But in general I think I kind of understand how it all works.
So I had a trigger function to add row to a profile table when a new user signs up, it was all working as expected when I only was adding the user ID and the username, but then I decided to add an svg to the avatar and I just can't figure out what I am doing wrong.
This is my function:
declare
svg text;
begin
select content
into svg
from http_get(concat('https://avatars.dicebear.com/api/bottts/', new.id::text, '.svg'));
insert into public.profile (id, username, avatar)
values (
new.id,
new.raw_user_meta_data ->> 'name',
svg
);
return new;
end;
I've done a lot of trial and error and if I remove the http_get and just insert a string into avatar like so and it works:
declare
svg text;
begin
svg := 'this works...';
insert into public.profile (id, username, avatar)
values (
new.id,
new.raw_user_meta_data ->> 'name',
svg
);
return new;
end;
and I've also tried to export the http_call into an external function:
declare
svg text;
begin
select content
into svg
from http_get(concat('https://avatars.dicebear.com/api/bottts/', seed, '.svg'));
return svg
end;
And call it, and it does return the svg html code as text as I would expect it to.
But then I add that function to the previous thing and it doesn't work:
insert into public.profile (id, username, avatar)
values (
new.id,
new.raw_user_meta_data ->> 'name',
get_dicebears_svg(new.id::text);
);
return new;
end;
I've also tried using cast(new.id as text) instead of new.id::text.
Could someone tell me what I am doing wrong?
Try adding
SET search_path = extensions, public, pg_temp;
Related
I am trying to fetch data from remote db by using dblink through function but getting an error "query has no destination for result data". I am using plpgsql language to do the same.
Function:
CREATE OR REPLACE FUNCTION fun()
RETURNS text AS
$$
begin
select dblink_connect(
'port=5432 dbname=test user=postgres password=****');
WITH a AS (
SELECT *
FROM dblink(
'SELECT slno,fname,mname,lname
FROM remote_tbl'
) AS t (slno int, fname text, mname text, lname text)
)
, b AS (
INSERT INTO temptab1
SELECT slno, name
FROM a
)
, c AS (
INSERT INTO temptab2
SELECT slno, name
FROM a
)
INSERT INTO temptab3
SELECT slno, name
FROM a;
select dblink_disconnect();
end;
$$
LANGUAGE plpgsql;
Calling Function:
select fun();
Error: query has no destination for result data
The stored procedure won't just return the result of the last SELECT. You need to actually return the value:
CREATE OR REPLACE FUNCTION fun() RETURNS text AS $$
BEGIN
--- ....
RETURN(SELECT dblink_disconnect());
END
$$ LANGUAGE plpgsql;
You're getting the error because Postgres expects the function to return something of type text, but your function doesn't return anything.
Use a plain SQL function instead of PL/PgSQL, or use SELECT INTO and ordinary RETURN.
Reason for the error you're getting is because there is no return in between your BEGIN and END for example:
BEGIN
update mytable set lastcodeused = to_char(cast(lastcodeused as INTEGER)+1, 'FM999999999999999999') where
classid = classIdVar and appid= appIdInt
RETURNING concat(pageUniqueCode,lastcodeused) as pageUniqueCode
into taskcode;
return taskcode;
END;
If you have this error using a pgplsql procedure or function, and you are sure that the return is defined correctly, there exists a different non-intuitive cause. I needed some time to realize this so I think it is worth sharing. I had a function like this:
CREATE OR REPLACE FUNCTION "db".fn_x(
id integer)
RETURNS TABLE(b_val varchar(100), c_val varchar(100))
LANGUAGE 'plpgsql'
AS $BODY$
DECLARE
var_b_val varchar(100);
var_c_val varchar(100);
BEGIN
select var_b, var_c
-- Missing INTO clause was the cause of the error.
var_b_val, var_c_val
from "db".table_y where y_id = id;
return query(select var_b_val, var_c_val);
END;
$BODY$;
Just adding that missing INTO clause made the function work correctly.
In conclusion, this error can also trigger on silent syntax errors.
I'm using a function in my Postgres and with this function, I want to delete a record using table and id and store the record as a JSON.
As you can imagine, I'm getting the table name as a parameter, but at that point, I don't know the name of the fields inside of my table. And I have:
create or replace function deletetable(_table text, _id int)
returns json as
$func$
declare
res json;
begin
execute format('SELECT json_to_recordset(::json) FROM ' || _table || ' WHERE id ' || id) into res;
-- HERE ONCE I GET THE RECORD AS JSON I HAVE TO INSERT IT IN MY TABLE
return res;
end
$func$ language plpgsql;
Well, my specific question is, how can I retrieve that information?
With my current function I get:
SELECT json_to_recordset(::json) FROM table
First create a log table:
create table logtable (
id serial primary key,
del_tname text not null,
del_ts timestamptz not null default now(),
del_rec jsonb
);
Your function should then look like this:
create or replace function deletetable(_table text, _id int)
returns jsonb as
$$
declare
res jsonb;
begin
execute format('with d as (delete from %I where id = %s returning *) '
'insert into logtable (del_tname, del_rec) '
'select %L, to_jsonb(d) from d returning del_rec',
_table, _id, _table) into res;
return res;
end
$$ language plpgsql;
Executing a select * from deletetable('tablename', 14); will delete your row, log it to logtable, and return the jsonb of the row.
I'm trying to put a trigger on a table. This table has a body column with type text. The text in this column can contain #{username} mentions and I need to extract them and write them to the database and also notify my application.
The code below works, but I don't think its ideal that in the foreach block, I'm doing an extra select to get the user_id of associated with the username. But the problem is, I can't figure out how to foreach over the mentions json as an array. I've tried many things but I just got error after error, and quite frankly I have a headache now!
It also seems weird to me that I have to have the usernames array just to extract the usernames and then put id,username,firebase_id in another array but I couldn't select both the result of parse_tokens and the id, firebase_id from the users table to a single array.
Any ideas?
Any other suggestions are welcome.
Thanks
create or replace function notif() returns trigger as $$
declare
usernames text[];
mentions json;
mu text;
begin
select parse_tokens(new.body, '#') into usernames;
select json_agg(tmp)
from (
select id, username, firebase_id
into mentions
from users
where username=any(usernames)
) tmp;
perform pg_notify('status', mentions::text);
foreach mu in array usernames loop
insert into mentions (status_id, from_user_id, to_user_id) values
(new.id, new.user_id, (select id from users where username=mu));
end loop;
return new;
end; $$ language plpgsql;
ok, I solved the problem. This does exactly what I want:
create type mention_info as (id integer, username varchar(64), firebase_id varchar(64));
create or replace function notif() returns trigger as $$
declare
mentions mention_info[];
m mention_info;
begin
select array_agg(tmp) into mentions from
(select id, username, firebase_id from users
where username=any(parse_tokens(new.body, '#'))) tmp;
perform pg_notify('status', mentions::text);
foreach m in array mentions loop
insert into mentions
(status_id, from_user_id, to_user_id) values (new.id, new.user_id, m.id);
end loop;
return new;
end;
$$ language plpgsql;
I am trying to create a complicated pl/pgsql function that gathers some results from a query and then checks each one and returns it or not.
This is my code so far. The record and loop part confuse me.
CREATE FUNCTION __a_search_creator(creator text, ordertype integer, orderdate integer, areaid bigint) RETURNS record
AS $$
DECLARE
fromText text;
whereText text;
usingText text;
firstrecord record;
areageom geometry;
BEGIN
IF areaid IS NOT NULL
THEN
EXECUTE format('SELECT area.geom FROM area WHERE area.id=$1')
INTO areageom
USING areaid;
FOR firstrecord IN
EXECUTE format(
'SELECT place.id, person.name, place.geom
FROM '||fromText||'
WHERE '||whereText)
USING creator, ordertype , orderdate
LOOP
--return only data that the place.geom is inside areageom using PostGIS
END LOOP;
END IF;
RETURN firstrecord;
END;
$$
LANGUAGE plpgsql;
I plan to do some extra checks in the loop, as you can see and RETURN only data that the place.geom (lon/lat) is inside areageom. But since I am new to pl/pgsql, I am creating now the first step, just gathering all the data, put them in a record and return.
My problem is that, no matter what I try I keep getting only one result back. I call select __a_search_creator('johnson',8, 19911109, 20); and I get 1,"seth johnson", 65485,84545 but I know I should be getting another row of results. Is there overwrite happening?
I tried putting RETURN NEXT firstrecord; I tried something like
select place.id from place INTO placeid;
select person.name from person INTO personname;
select place.geom from place INTO placegeom;
firstrecord.id := placeid;
firstrecord.name := personname;
firstrecord.geom := placegeom;
That still brings back just one result, I tried testing just this RAISE NOTICE '%', firstrecord.id; that still brings back just one full set of result.
I don't know how to proceed, please advice.
You declared your function with RETURNS record, so it returns a single record (of unknown type).
Use RETURNS SETOF record to return a set of rows. The manual:
The SETOF modifier indicates that the function will return a set of items, rather than a single item.
Better yet, use RETURNS TABLE with a proper table definition. Like you already had it in your recent questions:
Syntax error in dynamic SQL in pl/pgsql function
Using text in pl/pgsql brings empty set of results
Related (with detailed explanation and links to the manual):
How to return result of a SELECT inside a function in PostgreSQL?
Return setof record (virtual table) from function
Return a query from a function?
This is my solution on the problem
IF areaid IS NOT NULL
THEN
EXECUTE format('SELECT area.geom FROM area WHERE area.id=$1')
INTO areageom
USING areaid;
FOR firstrecord IN
EXECUTE format(
'SELECT place.id, person.name, place.geom FROM '||fromText||' WHERE '||whereText)
USING creator, ordertype, orderdate
LOOP
IF ST_Within(firstrecord.geom , areageom)
THEN
RETURN QUERY VALUES(firstrecord.id, firstrecord.name, firstrecord.geom);
END IF;
END LOOP;
END IF;
RETURN;
Thanks to #Erwin Brandstetter for the useful links.
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;