In postgres I have a custom function that will join first and last name.
CREATE FUNCTION full_name(u users) RETURNS varchar
LANGUAGE plpgsql VOLATILE
AS $$
BEGIN
RETURN CONCAT_WS(' ', u.first_name, u.last_name);
END;
$$;
It would be nice if I didn't have to set the the name of the column name and it was determined by the function eg.
eg. here I have to say the column name is full_name
SELECT
full_name(users) as full_name
But it would be nice if it atomically name it full_name
SELECT
full_name(users)
Is this possible to set in a custom function?
As the function parameter is the name of the table, you don't have to pass it.
You can call it like this:
select users.full_name
from users;
Note that you have to prefix the function name with the table name. You can also use an alias, but you still need the prefix.
select u.full_name
from users u;
In that case the column from the result set will be named full_name (the name of the function)
Btw: you don't need PL/pgSQL for the function. A plain SQL will likely be faster for this. Eespecially when you declare it stable instead of volatile - the it can be inlined and the overhead of calling the function is eliminated.
Try to use out parameter instead of returning the value:
CREATE FUNCTION full_name(in u users, out full_name varchar)
LANGUAGE plpgsql VOLATILE
AS $$
BEGIN
full_name := CONCAT_WS(' ', u.first_name, u.last_name);
END;
$$;
and
SELECT * FROM full_name(users)
This could be a solution to your problem. Instead of passing users as parameter you can use the first_name and last_name as parameters to the function.
CREATE OR REPLACE FUNCTION full_name(character varying ,character varying)
RETURNS character varying AS
$BODY$
declare
l_nom ALIAS FOR $1;
l_pnom ALIAS FOR $2;
begin
RETURN CONCAT_WS(' ', l_nom, l_pnom);
end;$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION full_name(character varying ,character varying) OWNER TO postgres;
SELECT full_name(u.first_name, u.last_name)
Related
I've created a function like this.
create or replace function pesquisar_imoveis_residenciais_venda()
returns table(preco decimal)
as $$
begin
select preco from casa_venda;
end; $$
language 'plpgsql';
When I call it
select pesquisar_imoveis_residenciais_venda()
I get the column reference preco is ambiguous.
I've visited some related question. But they are too difficult to follow, very complex functions.
The columns defined in the RETURNS QUERY clause are variables in the PL/pgSQL function body, so the ambiguity is between the variable preco and the table column of the same name. You need to qualify the reference with either the table name or the function name to disambiguate that.
But your function definition has other issues. I think you want:
create or replace function pesquisar_imoveis_residenciais_venda()
returns table(preco decimal)
as $$
begin
return query select cv.preco from casa_venda cv;
end; $$
language 'plpgsql';
select *
from pesquisar_imoveis_residenciais_venda();
Here is a db<>fiddle.
You can override the plpgsql.variable_conflict configuration parameter:
create or replace function pesquisar_imoveis_residenciais_venda()
returns table(preco decimal)
as $$
#variable_conflict use_column
begin
select preco from casa_venda;
end; $$
language 'plpgsql';
This assumes you want to use the column value and not the variable value. If that's not the case then specify #variable_conflict use_variable instead.
See the docs for further details.
it means column name Preco in table casa_venda is not exist or exist more than 1 time.
I have a table with multiple columns in PostgreSQL. I try to make a function returning a table with a few default columns and a variable column. The column name should be passed as function parameter. Example:
SELECT * FROM get_gas('temperature');
This is my code right now:
CREATE OR REPLACE FUNCTION get_gas(gas text)
RETURNS TABLE (id INTEGER, node_id INTEGER,
gas_name DOUBLE PRECISION,
measurement_timestamp timestamp without time zone )
AS
$$
BEGIN
SELECT measurements_lora.id, measurements_lora.node_id, gas, measurements_lora.measurement_timestamp
AS measure
FROM public.measurements_lora;
END
$$ LANGUAGE plpgsql;
When passing, for example, 'temperature' as column name (gas), I want to get a table with these columns from the function call.
id - node_id - temperature - measurement_timestamp
How would I achieve this?
You can use EXECUTE statement.
CREATE OR REPLACE FUNCTION get_gas(gas text) RETURNS TABLE (f1 INTEGER, f2 INTEGER, f3 DOUBLE PRECISION, f4 timestamp without time zone ) AS
$$
DECLARE
sql_to_execute TEXT;
BEGIN
SELECT 'SELECT measurements_lora.id,
measurements_lora.node_id, '
|| gas ||',
measurements_lora.measurement_timestamp AS measure
FROM public.measurements_lora '
INTO sql_to_execute;
RETURN QUERY EXECUTE sql_to_execute;
END
$$ LANGUAGE plpgsql;
This will create a variable sql_to_execute with your field and. QUERY EXECUTE will execute your interpreted query.
EDIT 1: Look at the another answer the concernings about security issues.
If you really need dynamic SQL in a PL/pgSQL function (which you don't), be sure to defend against SQL injection! Like:
CREATE OR REPLACE FUNCTION get_gas(gas text)
RETURNS TABLE (id integer
, node_id integer
, gas_name double precision
, measurement_timestamp timestamp)
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY EXECUTE format(
'SELECT m.id, m.node_id, m.%I, m.measurement_timestamp
FROM public.measurements_lora m'
, gas
);
END
$func$;
The format specifier %I in format() double-quotes identifiers where needed,
See:
SQL injection in Postgres functions vs prepared queries
Insert text with single quotes in PostgreSQL
I'm new to plpgsql and I'm trying to create function that will check if a certain value exists in table and if not will add a row.
CREATE OR REPLACE FUNCTION hire(
id_pracownika integer,
imie character varying,
nazwisko character varying,
miasto character varying,
pensja real)
RETURNS TEXT AS
$BODY$
DECLARE
wynik TEXT;
sprawdzenie INT;
BEGIN
sprawdzenie = id_pracownika;
IF EXISTS (SELECT id_pracownika FROM pracownicy WHERE id_pracownika=sprawdzenie) THEN
wynik = "JUZ ISTNIEJE";
RETURN wynik;
ELSE
INSERT INTO pracownicy(id_pracownika,imie,nazwisko,miasto,pensja)
VALUES (id_pracownika,imie,nazwisko,miasto,pensja);
wynik = "OK";
RETURN wynik;
END IF;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
The issue is that I'm getting errors saying that id_pracownika is a column name and a variable.
How to specify that "id_pracownika" in such context refers to column name?
Assuming id_pracownika is The PRIMARY KEY of the table. Or at least defined UNIQUE. (If it's not NOT NULL, NULL is a corner case.)
SELECT or INSERT
Your function is another implementation of "SELECT or INSERT" - a variant of the UPSERT problem, which is more complex in the face of concurrent write load than it might seem. See:
Is SELECT or INSERT in a function prone to race conditions?
With UPSERT in Postgres 9.5 or later
In Postgres 9.5 or later use UPSERT (INSERT ... ON CONFLICT ...) Details in the Postgres Wiki. This new syntax does a clean job:
CREATE OR REPLACE FUNCTION hire(
_id_pracownika integer
, _imie varchar
, _nazwisko varchar
, _miasto varchar
, _pensja real)
RETURNS text
LANGUAGE plpgsql AS
$func$
BEGIN
INSERT INTO pracownicy
( id_pracownika, imie, nazwisko, miasto, pensja)
VALUES (_id_pracownika,_imie,_nazwisko,_miasto,_pensja)
ON CONFLICT DO NOTHING;
IF FOUND THEN
RETURN 'OK';
ELSE
RETURN 'JUZ ISTNIEJE'; -- already exists
END IF;
END
$func$;
About the special variable FOUND:
Why is IS NOT NULL false when checking a row type?
Table-qualify column names to disambiguate where necessary. (You can also prefix function parameters with the function name, but that gets awkward quickly.)
But column names in the target list of an INSERT may not be table-qualified. Those are never ambiguous anyway.
Best avoid ambiguities a priori. Some (including me) like to prefix all function parameters and variables with an underscore.
If you positively need a column name as function parameter name, one way to avoid naming collisions is to use an ALIAS inside the function. One of the rare cases where ALIAS is actually useful.
Or reference function parameters by ordinal position: $1 for id_pracownika in this case.
If all else fails, you can decide what takes precedence by setting #variable_conflict. See:
Naming conflict between function parameter and result of JOIN with USING clause
There is more:
There are intricacies to the RETURNING clause in an UPSERT. See:
How to use RETURNING with ON CONFLICT in PostgreSQL?
String literals (text constants) must be enclosed in single quotes: 'OK', not "OK". See:
Insert text with single quotes in PostgreSQL
Assigning variables is comparatively more expensive than in other programming languages. Keep assignments to a minimum for best performance in plpgsql. Do as much as possible in SQL statements directly.
VOLATILE COST 100 are default decorators for functions. No need to spell those out.
Without UPSERT in Postgres 9.4 or older
...
IF EXISTS (SELECT FROM pracownicy p
WHERE p.id_pracownika = hire.id_pracownika) THEN
RETURN 'JUZ ISTNIEJE';
ELSE
INSERT INTO pracownicy(id_pracownika,imie,nazwisko,miasto,pensja)
VALUES (hire.id_pracownika,hire.imie,hire.nazwisko,hire.miasto,hire.pensja);
RETURN 'OK';
END IF;
...
But there is a tiny race condition between the SELECT and the INSERT, so not bullet-proof under heavy concurrent write-load.
In an EXISTS expression, the SELECT list does not matter. SELECT id_pracownika, SELECT 1, or even SELECT 1/0 - all the same. Just use an empty SELECT list. Only the existence of any qualifying row matters. See:
What is easier to read in EXISTS subqueries?
It is a example tested by me where I use EXECUTE to run a select and put its result in a cursor, using dynamic column names.
1. Create the table:
create table people (
nickname varchar(9),
name varchar(12),
second_name varchar(12),
country varchar(30)
);
2. Create the function:
CREATE OR REPLACE FUNCTION fun_find_people (col_name text, col_value varchar)
RETURNS void AS
$BODY$
DECLARE
local_cursor_p refcursor;
row_from_people RECORD;
BEGIN
open local_cursor_p FOR
EXECUTE 'select * from people where '|| col_name || ' LIKE ''' || col_value || '%'' ';
raise notice 'col_name: %',col_name;
raise notice 'col_value: %',col_value;
LOOP
FETCH local_cursor_p INTO row_from_people; EXIT WHEN NOT FOUND;
raise notice 'row_from_people.nickname: %', row_from_people.nickname ;
raise notice 'row_from_people.name: %', row_from_people.name ;
raise notice 'row_from_people.country: %', row_from_people.country;
END LOOP;
END;
$BODY$ LANGUAGE 'plpgsql'
3. Run the function
select fun_find_people('name', 'Cristian');
select fun_find_people('country', 'Chile');
inspire with Erwin Brandstetter's answers.
CREATE OR REPLACE FUNCTION test_upsert(
_parent_id int,
_some_text text)
RETURNS text
LANGUAGE plpgsql AS
$func$
DECLARE a text;
BEGIN
INSERT INTO parent_tree (parent_id, some_text)
VALUES (_parent_id,_some_text)
ON CONFLICT DO NOTHING
RETURNING 'ok' into a;
return a;
IF NOT FOUND THEN
return 'JUZ ISTNIEJE';
END IF;
END
$func$;
Follow Erwin's answer. I make a variable hold the return type text.
If conflict do nothing then the function will return nothing. For example, already have parent_id = 10, Then the result would be as following:
test_upsert
------------
(1 row)
NOT Sure the usage of:
IF NOT FOUND THEN
return 'JUZ ISTNIEJE';
END IF;
I've got a postgresql stored procedure, which is returning an integer.
When I call that function, the result is returned with the function name as column name.
For example the name of the function is: "add-person". The column name, when invoking the function, is "add-person".
Is there a way to make the database return the integer with a self-choosen column name? For example "id"?
I think it is pretty easy, but I currently miss the forests for the trees..
Edit:
What i'd missed to tell, is that the return value is a variable, like so:
CREATE OR REPLACE FUNCTION "scheme"."add-person"(arggivenname character varying, argfamilyname character varying) RETURNS integer AS
$BODY$
DECLARE
varResponse integer;
BEGIN
-- Operations before
INSERT INTO "scheme"."table"
(
given_name,
family_name
)
VALUES
(
arggivenname,
argfamilyname
)
RETURNING
"id"
INTO
varResponse;
-- Operations after
RETURN varResponse;
END;
$BODY$
LANGUAGE plpgsql VOLATILE COST 100;
You can us the AS statement for that. That means:
Select add-person() AS yourcolumnname
To have a named column from a function it is necessary to create a type and return that type from the function
create type mytype as (mycolumn integer);
create or replace function ri()
returns mytype as $$
select 1;
$$ language sql;
select * from ri();
mycolumn
----------
1
Edit
Or much simpler without the type creation as in #pozs comment:
create or replace function ri(out mycolumn integer)
as $$
select 1;
$$ language sql;
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;