plpgsql split string - plpgsql

I have a function like this:
FUNCTION addUser(name string, surname string) RETURNS text AS
and now, what I need is to take only 3 starting characteters of each argument and make a new string out of them and return it.
Can you point me in the right direction on how to do that?

left is awesome but only exists in >9.1. If you are stuck, like I am, on 8.3 or 8.4 you can use substr
CREATE FUNCTION addUser(IN in_name TEXT, IN in_surname TEXT) RETURNS TEXT AS
$$
SELECT substr($1,1,3) || substr($2,1,3);
$$
LANGUAGE SQL STABLE;

return left(name,3) || left(surname,3);
More details about string functions are in the manual:
http://www.postgresql.org/docs/current/static/functions-string.html

Related

Call a sequence through a function in PostgreSql

I have a sequence in the database, and I wanted to call the sequence through a function.
I have tried below, need an assistance to create the function in a standard process, as this is not working.
select nextval('code_seq')
CREATE FUNCTION code_sequence(integer) RETURNS integer
LANGUAGE SQL
IMMUTABLE
return select nextval('code_seq');
CREATE FUNCTION local_accounts_sequence(integer) RETURNS integer
LANGUAGE SQL
IMMUTABLE
RETURN (select nextval('code_seq'));
select local_accounts_sequence(1)
check out this solution
I am able to achieve using below statements,
CREATE FUNCTION code_sequence(out seq int)
LANGUAGE plpgsql
as $$
begin
select nextval('code_seq') into seq;
end;$$
try this :
CREATE FUNCTION local_accounts_sequence() RETURNS integer
AS 'SELECT nextval(''code_seq'');'
LANGUAGE SQL;
call it like this :
select local_accounts_sequence();

How to pass schema name dynamically in a function?

I have function called list_customers, taking i_entity_id, i_finyear as input params. The schema name is built from i_finyear, I need to execute the query based on the given schema.
I tried the below code:
CREATE OR REPLACE FUNCTION list_customers(i_entity_id integer,
i_finyear integer)
RETURNS TABLE(entity_id integer, client_id
integer, financial_yr integer) LANGUAGE 'plpgsql' AS
$BODY$
declare finyear integer := i_finyear;
schema_1 text := 'tds'||''||i_finyear;
begin
set search_path to schema_1;
return query select
d.entity_id, d.client_id, d.financial_yr
from schema_1.deductor d where d.entity_id = 1331;
end;
$BODY$;
Then:
select tds2020.list_customers(1331,2022);
imagelink
You need dynamic SQL with EXECUTE:
CREATE OR REPLACE FUNCTION list_customers(i_entity_id int, i_finyear int)
RETURNS TABLE (entity_id int, client_id int, financial_yr int)
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY EXECUTE
'SELECT d.entity_id, d.client_id, d.financial_yr
FROM tds' || i_finyear || '.deductor d
WHERE d.entity_id = $1'
USING i_entity_id;
END
$func$;
Since the input parameter i_finyear is type integer, there is no danger of SQL injection and you can use plain concatenation to concatenate your schema name like "tbl2016". Else, you'd use format() to defend against that. See:
Table name as a PostgreSQL function parameter
You can also concatenate (properly quoted) values, but it's safer and more efficient to pass the value with the USING keyword. See:
Replace double quotes with single quotes in Postgres (plpgsql)
No need to change the search_path additionally. That would just add an expensive context switch.

PLpgSQL function not returning matching titles

I am trying to return the movie name and the number of cast and crew when given a text. When I input the string and am using ilike, my query returns no matching titles. I created a view previously that has the movie titles and the number of crew to be input in the function.
My code is:
create or replace view movies_crew as
select movies.id, movies.title, principals.role
from movies
join principals on principals.movie_id=movies.id
where principals.role <> 'producer'
;
create or replace view movie_makers as
select movies_crew.title, count(movies_crew.title) as ncrew
from movies_crew
where movies_crew.title = 'Fight Club'
group by movies_crew.title;
CREATE or REPLACE function Q11(partial_title text)
RETURNS SETOF text
AS $$
DECLARE
title text;
BEGIN
for title in
select movie_makers.title, movie_makers.ncrew
from movie_makers
where movie_makers.title ilike '%$1%'
loop
return next movie_makers.title||'has'||movie_makers.ncrew||'cast and crew';
end loop;
if(not found) then
return next 'No matching titles';
end if;
END;
$$ LANGUAGE plpgsql;
select * from q11('Fight Club')
My database is: https://drive.google.com/file/d/1NVRLiYBVbKuiazynx9Egav7c4_VHFEzP/view?usp=sharing
Your immediate quoting issue aside (has been addressed properly by Jeff), the function can be much simpler and faster like this:
CREATE or REPLACE FUNCTION q11(partial_title text)
RETURNS SETOF text
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY
SELECT m.title || ' has ' || m.ncrew || ' cast and crew'
FROM movie_makers m
WHERE m.title ~* $1;
IF NOT FOUND THEN
RETURN NEXT 'No matching titles';
END IF;
END
$func$;
Major points:
Your function was still broken. References to movie_makers.title and movie_makers.ncrew wouldn't work that way. I fixed it.
Use RETURN QUERY instead of the loop. This way we also do not need to use or even declare any variables at all. See:
How to return result of a SELECT inside a function in PostgreSQL?
Optionally use the case insensitive regular expression match operator ~*. (Simpler, not faster.)
Difference between LIKE and ~ in Postgres
Either way, you may want to escape special characters. See:
Escape function for regular expression or LIKE patterns
Aside: hardly makes sense to filter on a view that already selects 'Fight Club' as its only row. For a meaningful search, you wouldn't use these views ...
ilike '%$1%'
$1 is not interpolated when inside single quotes, so you are searching for the literal characters $ and 1.
You could instead do:
ilike '%'||$1||'%'

How to cast string value to enum

I have a table with an enum type in it, and I created a function to add data to that table. I want that function to be generous in what to accept, so I take a text as the enum type and want to cast it later.
This is the enum:
CREATE TYPE public.enum_log_priority AS ENUM (
'critical','error','warning','notice','debug'
);
And this is the function:
CREATE OR REPLACE FUNCTION public.log_write(
_message text,
_priority text
) RETURNS integer AS
$body$
BEGIN
_priority = lower(_priority);
INSERT INTO log (message, priority) VALUES (_message, _priority);
RETURN 0;
END
$body$
LANGUAGE 'plpgsql';
I know this doesn't work:
ERROR: column "priority" is of type enum_log_priority but expression is of type text
but how can I do this?
Use syntax like below during insertion
'critical'::enum_log_priority
Please see some link as well
http://www.postgresql.org/docs/9.1/static/datatype-enum.html
Inserting into custom SQL types with prepared statements in java
java enum and postgresql enum
change your function like this:
CREATE OR REPLACE FUNCTION public.log_write(
_message text,
_priority text
) RETURNS integer AS
$body$
BEGIN
_priority = lower(_priority);
INSERT INTO log (message, priority) VALUES (_message, _priority::enum_log_priority);
RETURN 0;
END
$body$
LANGUAGE 'plpgsql';
| sql fiddle demo |
Postgres supports also the cast function:
cast(priority AS enum_log_priority);
Consider that sometimes (for example in Prisma raw queries) you need to put the enum type inside quotations.
'critical'::"enum_log_priority"

How to find the first and last occurrences of a specific character inside a string in PostgreSQL

I want to find the first and the last occurrences of a specific character inside a string. As an example, consider a string named "2010-####-3434", and suppose the character to be searched for is "#". The first occurrence of hash inside the string is at 6-th position, and the last occurrence is at 9-th position.
Well...
Select position('#' in '2010-####-3434');
will give you the first.
If you want the last, just run that again with the reverse of your string. A pl/pgsql string reverse can be found here.
Select length('2010-####-3434') - position('#' in reverse_string('2010-####-3434')) + 1;
My example:
reverse(substr(reverse(newvalue),0,strpos(reverse(newvalue),',')))
Reverse all string
Substring string
Reverse result
In the case where char = '.', an escape is needed. So the function can be written:
CREATE OR REPLACE FUNCTION last_post(text,char)
RETURNS integer LANGUAGE SQL AS $$
select length($1)- length(regexp_replace($1, E'.*\\' || $2,''));
$$;
9.5+ with array_positions
Using basic PostgreSQL array functions we call string_to_array(), and then feed that to array_positions() like this array_positions(string_to_array(str,null), c)
SELECT
arrpos[array_lower(arrpos,1)] AS first,
arrpos[array_upper(arrpos,1)] AS last
FROM ( VALUES
('2010-####-3434', '#')
) AS t(str,c)
CROSS JOIN LATERAL array_positions(string_to_array(str,null), c)
AS arrpos;
I do not know how to do that, but the regular expression functions like regexp_matches, regexp_replace, and regexp_split_to_array may be an alternative route to solving your problem.
This pure SQL function will provide the last position of a char inside the string, counting from 1. It returns 0 if not found ... But (big disclaimer) it breaks if the character is some regex metacharacter ( .$^()[]*+ )
CREATE FUNCTION last_post(text,char) RETURNS integer AS $$
select length($1)- length(regexp_replace($1, '.*' || $2,''));
$$ LANGUAGE SQL IMMUTABLE;
test=# select last_post('hi#-#-#byte','#');
last_post
-----------
7
test=# select last_post('hi#-#-#byte','a');
last_post
-----------
0
A more robust solution would involve pl/pgSQL, as rfusca's answer.
Another way to count last position is to slit string to array by delimeter equals to needed character and then substract length of characters
for the last element from the length of whole string
CREATE FUNCTION last_pos(char, text) RETURNS INTEGER AS
$$
select length($2) - length(a.arr[array_length(a.arr,1)])
from (select string_to_array($2, $1) as arr) as a
$$ LANGUAGE SQL;
For the first position it is easier to use
select position('#' in '2010-####-3434');