Prepared Statement Does Not Exists, PostgreSQL - postgresql

I created the stored procedure with this code
CREATE PROCEDURE get_conferences_for_attendee
(
IN start_time TIMESTAMP,
IN end_time TIMESTAMP,
IN email VARCHAR(255),
IN deleted BOOLEAN
)
AS
$$
SELECT c.localuuid, c.title, i.id, i.start_time, i.end_time, i.status, a.email, a.deleted
FROM Conference c
INNER JOIN Instance i ON i.conference_localuuid = c.localuuid
INNER JOIN Conference_Attendees ca ON ca.conference_localuuid = c.localuuid
INNER JOIN Attendee a ON ca.attendees_localuuid = a.localuuid
WHERE i.start_time BETWEEN start_time AND end_time
AND a.email = email
AND a.deleted = deleted
$$ LANGUAGE SQL;
and this returned
CREATE PROCEDURE
I can see my procedure
SELECT proname, prorettype
FROM pg_proc
WHERE pronamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'public');
proname | prorettype
------------------------------+------------
get_conferences_for_attendee | 2278
When I try to execute, I get the error on the title.
EXECUTE get_conferences_for_attendee ('2022-12-26T00:00:00', '2023-01-01T23:59:59', 'yacs.demo2#abc.com', false);
ERROR: prepared statement "get_conferences_for_attendee" does not exist
Update
I found a solution but I'm not sure if it's the proper way to create this. It looks too complicated for me.
CREATE TYPE conference_record AS (
localuuid VARCHAR(255),
title VARCHAR(255),
id VARCHAR(255),
start_time TIMESTAMP,
end_time TIMESTAMP,
status VARCHAR(255),
email VARCHAR(255),
deleted BOOLEAN
);
CREATE FUNCTION get_conferences_for_attendee
(
IN start_time TIMESTAMP,
IN end_time TIMESTAMP,
IN email VARCHAR(255),
IN deleted BOOLEAN
)
RETURNS SETOF conference_record AS $$
BEGIN
RETURN QUERY
SELECT c.localuuid, c.title, i.id, i.start_time, i.end_time, i.status, a.email, a.deleted
FROM Conference c
INNER JOIN Instance i ON i.conference_localuuid = c.localuuid
INNER JOIN Conference_Attendees ca ON ca.conference_localuuid = c.localuuid
INNER JOIN Attendee a ON ca.attendees_localuuid = a.localuuid
WHERE i.start_time BETWEEN $1 AND $2
AND a.email = $3
AND a.deleted = $4;
END;
$$ LANGUAGE plpgsql;
SELECT * FROM get_conferences_for_attendee ('2022-12-26T00:00:00', '2023-01-01T23:59:59', 'yacs.demo1#abc.com', false);

As pointed out in the comments, to use a procedure, you need to CALL your_procedure();.
The code you presented looks like you're trying to get something from it, so a function is more suitable - procedures can return data through out and inout parameters or side-effects, like dumping them to an outside table.
The function and type definitions you later added in an edit look fine. If you're planning to feed it directly into a table, you don't need to define the custom type and instead specify RETURNS SETOF your_target_table_name or RETURNS TABLE (LIKE your_target_table_name).
You can also make it LANGUAGE sql - since you're not using anything plpgsql-specific, you don't need the additional overhead that comes with it. You'll just have to remove BEGIN RETURN QUERY and END, leaving just the bare-bones query.
You can also use a regular prepared statement for this:
PREPARE get_conferences_for_attendee(
TIMESTAMP,
TIMESTAMP,
VARCHAR(255),
BOOLEAN ) AS
SELECT
c.localuuid,
c.title,
i.id,
i.start_time,
i.end_time,
i.status,
a.email,
a.deleted
FROM Conference c
INNER JOIN Instance i ON i.conference_localuuid = c.localuuid
INNER JOIN Conference_Attendees ca ON ca.conference_localuuid = c.localuuid
INNER JOIN Attendee a ON ca.attendees_localuuid = a.localuuid
WHERE i.start_time BETWEEN $1 AND $2
AND a.email = $3
AND a.deleted = $4;
And use it exactly like you intially planned to, with an EXECUTE:
EXECUTE get_conferences_for_attendee(
'2022-12-26T00:00:00',
'2023-01-01T23:59:59',
'yacs.demo1#abc.com',
false);
Online demo

I found a solution but I'm not sure if it's the proper way to create this.
A function is the correct way to do this.
It looks too complicated for me.
You are indeed over-complicating the implementation. You don't need to create a type, this can be simplified by using returns table() instead.
You also don't need PL/pgSQL for this. A SQL function will be enough
CREATE FUNCTION get_conferences_for_attendee
(
p_start_time TIMESTAMP,
p_end_time TIMESTAMP,
p_email text,
p_deleted BOOLEAN
)
RETURNS table(localuuid text, title, text, id text, start_time timestamp, end_time timestamp, status text, email text, deleted boolean)
AS
$$
SELECT c.localuuid, c.title, i.id, i.start_time, i.end_time, i.status, a.email, a.deleted
FROM Conference c
INNER JOIN Instance i ON i.conference_localuuid = c.localuuid
INNER JOIN Conference_Attendees ca ON ca.conference_localuuid = c.localuuid
INNER JOIN Attendee a ON ca.attendees_localuuid = a.localuuid
WHERE i.start_time BETWEEN p_start_time AND p_end_time
AND a.email = p_email
AND a.deleted = p_deleted
$$
LANGUAGE sql
stable;
I renamed the parameters with a prefix to avoid a name clash with columns of the same name.
Note that using BETWEEN with timestamp values is usually a bad idea. It's better to use a range query using >= for the lower bound and < for the "next day" of the upper bound
e.g. start_time >= 2022-12-26 00:00:00' and end_time < '2023-01-02 00:00:00'
Your condition would not return rows where the end_time is e.g. 2023-01-01 23:59:59.999

Related

PgSQL function returning table and extra data computed in process

In PgSQL I make huge select, and then I want count it's size and apply some extra filters.
execute it twice sound dumm,
so I wrapped it in function
and then "cache" it and return union of filtered table and extra row at the end where in "id" column store size
with q as (select * from myFunc())
select * from q
where q.distance < 400
union all
select count(*) as id, null,null,null
from q
but it also doesn't look like proper solution...
and so the question: is in pg something like "generator function" or any other stuff that can properly solve this ?
postgreSQL 13
myFunc aka "selectItemsByRootTag"
CREATE OR REPLACE FUNCTION selectItemsByRootTag(
in tag_name VARCHAR(50)
)
RETURNS table(
id BIGINT,
name VARCHAR(50),
description TEXT,
/*info JSON,*/
distance INTEGER
)
AS $$
BEGIN
RETURN QUERY(
WITH RECURSIVE prod AS (
SELECT
tags.name, tags.id, tags.parent_tags
FROM
tags
WHERE tags.name = (tags_name)
UNION
SELECT c.name, c.id , c.parent_tags
FROM
tags as c
INNER JOIN prod as p
ON c.parent_tags = p.id
)
SELECT
points.id,
points.name,
points.description,
/*points.info,*/
points.distance
from points
left join tags on points.tag_id = tags.id
where tags.name in (select prod.name from prod)
);
END;
$$ LANGUAGE plpgsql;
as a result i want see maybe set of 2 table or generator function that yield some intermediate result not shure how exacltly it should look
demo
CREATE OR REPLACE FUNCTION pg_temp.selectitemsbyroottag(tag_name text, _distance numeric)
RETURNS TABLE(id bigint, name text, description text, distance numeric, count bigint)
LANGUAGE plpgsql
AS $function$
DECLARE _sql text;
BEGIN
_sql := $p1$WITH RECURSIVE prod AS (
SELECT
tags.name, tags.id, tags.parent_tags
FROM
tags
WHERE tags.name ilike '%$p1$ || tag_name || $p2$%'
UNION
SELECT c.name, c.id , c.parent_tags
FROM
tags as c
INNER JOIN prod as p
ON c.parent_tags = p.id
)
SELECT
points.id,
points.name,
points.description,
points.distance,
count(*) over ()
from points
left join tags on points.tag_id = tags.id
where tags.name in (select prod.name from prod)
and points.distance > $p2$ || _distance
;
raise notice '_sql: %', _sql;
return query execute _sql;
END;
$function$
You can call it throug following way
select * from pg_temp.selectItemsByRootTag('test',20);
select * from pg_temp.selectItemsByRootTag('test_8',20) with ORDINALITY;
The 1 way to call the function, will have a row of total count total number of rows. Second way call have number of rows plus a serial incremental number.
I also make where q.distance < 400 into function input argument.
selectItemsByRootTag('test',20); means that q.distance > 20 and tags.name ilike '%test%'.

How to resolve error: return columns doesn't match expected columns?

I write function in PostgreSQL. I am trying to fetch data from db by passing parameters in function .But i am getting error" ERROR: structure of query does not match function result type".
Here i created a function in which i am using union to get unique values.In first select stat. i amsetting value for account total that is used in below code.
Function:
CREATE OR REPLACE FUNCTION GetDeptListForViewModifyJointUsePercentages ( p_nInstID numeric,p_nDeptID numeric)
RETURNS Table(
res_ndept_id numeric,
res_salaryPercent character varying,
res_nclient_cpc_mapping_id numeric,
res_CPCCODE character varying,
res_sdept_name character varying,
res_sclient_dept_id character varying,
res_sAlternateJointUsePercentage character varying
)
AS $$
declare v_AccountTotal numeric(18,2);
BEGIN
RETURN QUERY
select SUM(CAST (coalesce(eam.npayroll_amt, null) AS numeric(18,2))) as AccountTotal
FROM Account acct
INNER JOIN employeeaccountmapping eam
ON eam.nacct_id = acct.naccount_id
AND acct.ninst_id =p_nInstID
where acct.ndept_id =p_nDeptID ;
SELECT *
FROM
(select dep.ndept_id ,( CASE
WHEN v_AccountTotal =0 THEN 0
ELSE Round(SUM(CAST(coalesce(eam.npayroll_amt, null) AS numeric(18,2)) * cac.npercentage_assigned/100) / v_AccountTotal *100,null)
END
) as salaryPercent
,cac.nclient_cpc_mapping_id,client.sclient_cpc AS CPCCODE,
dep.sdept_name,dep.sclient_dept_id,'NO' ASsAlternateJointUsePercentage
FROM account
INNER JOIN employeeaccountmapping eam
ON eam.nacct_id = account .naccount_id AND account .ninst_id=p_nInstID
INNER JOIN accountcpcmapping acm
ON acm.naccount_cpc_mapping_id = account.naccount_cpc_mapping_id
INNER JOIN cpcaccountcpcmapping as cac
ON cac.naccount_cpc_mapping_id=acm.naccount_cpc_mapping_id
INNER JOIN clientcostpoolcodes as client
ON client.nclient_cpc_mapping_id=cac.nclient_cpc_mapping_id
INNER JOIN mastercostpoolcodes
ON mastercostpoolcodes .nmaster_cpc_id= client.nmaster_cpc_id
INNER JOIN department as dep
ON account.ndept_id=dep.ndept_id and dep.balternate_jointuse_percentage=FALSE
where account.ndept_id =p_nDeptID
Group by dep.ndept_id,cac.nclient_cpc_mapping_id,client.sclient_cpc
,dep.sdept_name,dep.sclient_dept_id, dep.balternate_jointuse_percentage
UNION
SELECT Department_1.ndept_id, a_1.SumByCPC, clientcostpoolcodes.nclient_cpc_mapping_id, clientcostpoolcodes .sclient_cpc, Department_1.sdept_name, Department_1.sclient_dept_id , 'YES' AS sAlternateJointUsePercentage
FROM department AS Department_1 LEFT OUTER JOIN
mastercostpoolcodes RIGHT OUTER JOIN
clientcostpoolcodes RIGHT OUTER JOIN
(SELECT JointUseStatistics_1.nccp_code, SUM(JointUseStatistics_1.npercent) /
(SELECT SUM(jointusestatistics.npercent) AS SumByCPC
FROM jointusestatistics INNER JOIN
roomdepartmentmapping ON jointusestatistics.nroom_allocation_id = roomdepartmentmapping .nroom_allocation_id
WHERE (roomdepartmentmapping .ndept_id = Room_1.ndept_id)) * 100 AS SumByCPC,
Room_1.ndept_id
FROM jointusestatistics JointUseStatistics_1 INNER JOIN roomdepartmentmapping AS Room_1 ON JointUseStatistics_1.nroom_allocation_id = Room_1.nroom_allocation_id
GROUP BY JointUseStatistics_1.nccp_code, JointUseStatistics_1.npercent, Room_1.ndept_id) AS a_1 ON
clientcostpoolcodes .nclient_cpc_mapping_id = a_1.nccp_code ON mastercostpoolcodes .nmaster_cpc_id = clientcostpoolcodes .nmaster_cpc_id ON
Department_1.ndept_id = a_1.ndept_id
WHERE (Department_1.balternate_jointuse_percentage = 'TRUE')
AND (Department_1.ninst_id= p_nInstID)
)AS My
where (ndept_id= p_nDeptID )
ORDER BY My.sdept_name,My.CPCCODE ;
END;
$$ LANGUAGE plpgsql;
I think what you want is the first query to just retrieve the count, store that in your variable, then use that variable in the second query which is the actual source of the result of the function.
So the first query should not use return query but select .. into .. store put the result into the variable.
Then you can prefix the second query with return query to return its result:
CREATE OR REPLACE FUNCTION GetDeptListForViewModifyJointUsePercentages ( p_nInstID numeric,p_nDeptID numeric)
RETURNS Table(
res_ndept_id numeric,
res_salaryPercent character varying,
res_nclient_cpc_mapping_id numeric,
res_CPCCODE character varying,
res_sdept_name character varying,
res_sclient_dept_id character varying,
res_sAlternateJointUsePercentage character varying
)
AS $$
declare
v_AccountTotal numeric(18,2);
BEGIN
-- no RETURN query here!!
select SUM(CAST (coalesce(eam.npayroll_amt, null) AS numeric(18,2)))
into v_accounttotal --<<< store result in variable
FROM Account acct
INNER JOIN employeeaccountmapping eam
ON eam.nacct_id = acct.naccount_id
AND acct.ninst_id =p_nInstID
where acct.ndept_id = p_nDeptID;
-- this is the query that should be returned
RETURN QUERY
SELECT *
FROM (
select dep.ndept_id,
CASE WHEN v_AccountTotal = 0 THEN 0
ELSE Round(SUM(CAST(coalesce(eam.npayroll_amt, null) AS numeric(18,2)) * cac.npercentage_assigned/100) / v_AccountTotal *100,null)
END as salaryPercent,
cac.nclient_cpc_mapping_id,
client.sclient_cpc AS CPCCODE,
dep.sdept_name,
dep.sclient_dept_id,
'NO' AS sAlternateJointUsePercentage
FROM account
INNER JOIN employeeaccountmapping eam
ON eam.nacct_id = account .naccount_id AND account .ninst_id=p_nInstID
INNER JOIN accountcpcmapping acm
ON acm.naccount_cpc_mapping_id = account.naccount_cpc_mapping_id
INNER JOIN cpcaccountcpcmapping as cac
ON cac.naccount_cpc_mapping_id=acm.naccount_cpc_mapping_id
INNER JOIN clientcostpoolcodes as client
ON client.nclient_cpc_mapping_id=cac.nclient_cpc_mapping_id
INNER JOIN mastercostpoolcodes
ON mastercostpoolcodes .nmaster_cpc_id= client.nmaster_cpc_id
INNER JOIN department as dep
ON account.ndept_id=dep.ndept_id and dep.balternate_jointuse_percentage=FALSE
where account.ndept_id =p_nDeptID
Group by dep.ndept_id,cac.nclient_cpc_mapping_id,client.sclient_cpc
,dep.sdept_name,dep.sclient_dept_id, dep.balternate_jointuse_percentage
UNION
SELECT Department_1.ndept_id, a_1.SumByCPC, clientcostpoolcodes.nclient_cpc_mapping_id,
clientcostpoolcodes .sclient_cpc, Department_1.sdept_name, Department_1.sclient_dept_id,
'YES' AS sAlternateJointUsePercentage
FROM department AS Department_1
LEFT OUTER JOIN mastercostpoolcodes --<<< missing join condition !!
RIGHT OUTER JOIN clientcostpoolcodes --<<< missing join condition !!
RIGHT OUTER JOIN (
SELECT JointUseStatistics_1.nccp_code,
SUM(JointUseStatistics_1.npercent) /
(SELECT SUM(jointusestatistics.npercent) AS SumByCPC
FROM jointusestatistics
INNER JOIN roomdepartmentmapping ON jointusestatistics.nroom_allocation_id = roomdepartmentmapping .nroom_allocation_id
WHERE (roomdepartmentmapping .ndept_id = Room_1.ndept_id)) * 100 AS SumByCPC,
Room_1.ndept_id
FROM jointusestatistics JointUseStatistics_1
INNER JOIN roomdepartmentmapping AS Room_1 ON JointUseStatistics_1.nroom_allocation_id = Room_1.nroom_allocation_id
GROUP BY JointUseStatistics_1.nccp_code, JointUseStatistics_1.npercent, Room_1.ndept_id
) AS a_1 ON clientcostpoolcodes.nclient_cpc_mapping_id = a_1.nccp_code
ON mastercostpoolcodes.nmaster_cpc_id = clientcostpoolcodes.nmaster_cpc_id --<< that should be somewhere else
ON Department_1.ndept_id = a_1.ndept_id --<< ??????
WHERE (Department_1.balternate_jointuse_percentage = 'TRUE')
AND (Department_1.ninst_id= p_nInstID)
) AS My
where (ndept_id= p_nDeptID )
ORDER BY My.sdept_name,My.CPCCODE;
END;
$$ LANGUAGE plpgsql;
Note that there are several syntax errors in your second query because the join conditions are scattered around the query. I have tried to highlight them.
In Postgresql, function returns a single value. But in your example, you are trying to return multiple outputs in terms of number of rows and columns.
You can alternatively use a stored procedure and save the output in a temporary table. Later use that table for your use.

I am getting Dollar sign unterminated

I want to create a function like below which inserts data as per the input given. But I keep on getting an error about undetermined dollar sign.
CREATE OR REPLACE FUNCTION test_generate
(
ref REFCURSOR,
_id INTEGER
)
RETURNS refcursor AS $$
DECLARE
BEGIN
DROP TABLE IF EXISTS test_1;
CREATE TEMP TABLE test_1
(
id int,
request_id int,
code text
);
IF _id IS NULL THEN
INSERT INTO test_1
SELECT
rd.id,
r.id,
rd.code
FROM
test_2 r
INNER JOIN
raw_table rd
ON
rd.test_2_id = r.id
LEFT JOIN
observe_test o
ON
o.raw_table_id = rd.id
WHERE o.id IS NULL
AND COALESCE(rd.processed, 0) = 0;
ELSE
INSERT INTO test_1
SELECT
rd.id,
r.id,
rd.code
FROM
test_2 r
INNER JOIN
raw_table rd
ON rd.test_2_id = r.id
WHERE r.id = _id;
END IF;
DROP TABLE IF EXISTS tmp_test_2_error;
CREATE TEMP TABLE tmp_test_2_error
(
raw_table_id int,
test_2_id int,
error text,
record_num int
);
INSERT INTO tmp_test_2_error
(
raw_table_id,
test_2_id,
error,
record_num
)
SELECT DISTINCT
test_1.id,
test_1.test_2_id,
'Error found ' || test_1.code,
0
FROM
test_1
WHERE 1 = 1
AND data_origin.id IS NULL;
INSERT INTO tmp_test_2_error
SELECT DISTINCT
test_1.id,
test_1.test_2_id,
'Error found ' || test_1.code,
0
FROM
test_1
INNER JOIN
data_origin
ON
data_origin.code = test_1.code
WHERE dop.id IS NULL;
DROP table IF EXISTS test_latest;
CREATE TEMP TABLE test_latest AS SELECT * FROM observe_test WHERE 1 = 2;
INSERT INTO test_latest
(
raw_table_id,
series_id,
timestamp
)
SELECT
test_1.id,
ds.id AS series_id,
now()
FROM
test_1
INNER JOIN data_origin ON data_origin.code = test_1.code
LEFT JOIN
observe_test o ON o.raw_table_id = test_1.id
WHERE o.id IS NULL;
CREATE TABLE latest_observe_test as Select * from test_latest where 1=0;
INSERT INTO latest_observe_test
(
raw_table_id,
series_id,
timestamp,
time
)
SELECT
t.id,
ds.id AS series_id,
now(),
t.time
FROM
test_latest t
WHERE t.series_id IS DISTINCT FROM observe_test.series_id;
DELETE FROM test_2_error re
USING t
WHERE t.test_2_id = re.test_2_id;
INSERT INTO test_2_error (test_2_id, error, record_num)
SELECT DISTINCT test_2_id, error, record_num FROM tmp_test_2_error ORDER BY error;
UPDATE raw_table AS rd1
SET processed = case WHEN tre.raw_table_id IS null THEN 2 ELSE 1 END
FROM test_1 tr
LEFT JOIN
tmp_test_2_error tre ON tre.raw_table_id = tr.id
WHERE rd1.id = tr.id;
OPEN ref FOR
SELECT 1;
RETURN ref;
OPEN ref for
SELECT o.* from observe_test o
;
RETURN ref;
OPEN ref FOR
SELECT
rd.id,
ds.id AS series_id,
now() AS timestamp,
rd.time
FROM test_2 r
INNER JOIN raw_table rd ON rd.test_2_id = r.id
INNER JOIN data_origin ON data_origin.code = rd.code
WHERE o.id IS NULL AND r.id = _id;
RETURN ref;
END;
$$ LANGUAGE plpgsql VOLATILE COST 100;
I am not able to run this procedure.
Can you please help me where I have done wrong?
I am using squirrel and face the same question as you.
until I found that:
-- Note that if you want to create the function under Squirrel SQL,
-- you must go to Sessions->Session Properties
-- then SQL tab and change the Statement Separator from ';' to something else
-- (for intance //). Otherwise Squirrel SQL sends one piece to the server
-- that stops at the first encountered ';', and the server cannot make
-- sense of it. With the separator changed as suggested, you type everything
-- as above and end with
-- ...
-- end;
-- $$ language plpgsql
-- //
--
-- You can then restore the default separator, or use the new one for
-- all queries ...
--

Postgres Stored Procedure issue - how do I solve this?

I am creating a stored procedure that looks like this:
CREATE FUNCTION get_components(_given_user_id integer) RETURNS TABLE (id integer, name varchar, active boolean, parent integer, type smallint, description varchar, email varchar, user_id integer, component_id integer, flag smallint) AS $body$
BEGIN
RETURN QUERY SELECT s.* FROM st_components s LEFT JOIN (SELECT a.* FROM st_users_components_perms a WHERE a.user_id=_given_user_id) ON a.component_id=s.id ORDER BY name;
END;
$body$ LANGUAGE plpgsql;
The problem is, ON a.component_id=s.id ORDER BY name doesn't work because a.component_id is out of scope at this point. Is there a way to declare "a" as st_users_components_perms outside of the query? How is this solved? Thank you very much for any insight!
SELECT
s.*
FROM
st_components s
LEFT JOIN
(SELECT
a.*
FROM
st_users_components_perms a
WHERE
a.user_id = <CONSTANT>
) AS x
ON
x.component_id = s.id
ORDER BY
name;
Though the query seems pointless but let's assume it's a simplified version of your real query.

Postgresql Slow on custom function, php but fast if directly input on psql using text search with gin index

I have 3 tables Person, Names, and Notes. Each person has multiple name and has optional notes. I have full text search on some columns on names and notes (see below), they are working perfectly if the word I search with is in the result set or is in the db, this is for custom function, php, and psql. The problem now is that when the word I search is not present in the db the query gets super slow in php and custom function but still fast on psql. On psql it's less than 1s, others are more than 10s.
Tables:
Person | id, birthday
Name | person_id, name, fs_name
Notes | person_id, note, fs_note
Beside PK and FK index, Gin index on fs_name and fs_note.
Function/Query
create or replace function queryNameFunc (TEXT)
returns TABLE(id int, name TEXT) as $$
select id, name
from person_name pnr
inner join person pr on (pnr.person_id=pr.id)
left join personal_notes psr on (psr.person_id = pr.id)
where pr.id in
(select distinct(id)
from person_name pn
inner join person p on (p.id = pn.person_id)
left join personal_notes ps on (ps.person_id = p.id)
where tname ## to_tsquery($1)
limit 20);
$$ language SQL;
The where condition is trimmed down in here, so for example if I do 'john & james' on $1 and the data is on the db then results is fast but if 'john and james' are not in db then its slow. This got slower as I have 1M records on person and 3M+ on names (all dummy records). Any idea on how to fix this? I tried restarting the server, restarting postgresql.
The database has to preprare the inner query before it has any knowledge about the parameter. This might result in a bad queryplan. To avoid this problem in a function, use the plpgsql-language and use EXECUTE inside the function:
CREATE OR REPLACE FUNCTION queryNameFunc (TEXT) RETURNS TABLE(id INT, name TEXT) AS $$
BEGIN
RETURN QUERY EXECUTE '
SELECT
id,
name
FROM
person_name pnr
INNER JOIN person pr ON (pnr.person_id=pr.id)
LEFT JOIN personal_notes psr ON (psr.person_id = pr.id)
WHERE
pr.id IN(
SELECT
DISTINCT(id)
FROM
person_name pn
INNER JOIN person p ON (p.id = pn.person_id)
LEFT JOIN personal_notes ps ON (ps.person_id = p.id)
WHERE tname ## to_tsquery($1)
LIMIT 20)' USING $1;
END;
$$ LANGUAGE plpgsql;
This works in version 8.4 and you do have to install plpgsql:
CREATE LANGUAGE plpgsql;