I would do it like this:
CREATE OR REPLACE FUNCTION list( _category varchar(100) , _limit int , _offset int , _order_by varchar(100) , _order_asc_desc text = 'ASC') -- last param with default value RETURNS TABLE(id int, name varchar, clientname varchar, totalcount bigint) LANGUAGE plpgsql AS $func$ DECLARE _empty text := ''; BEGIN -- Assert valid _order_asc_desc IF upper(_order_asc_desc) IN ('ASC', 'DESC', 'ASCENDING', 'DESCENDING') THEN -- proceed ELSE RAISE EXCEPTION 'Unexpected value for parameter _order_asc_desc. Allowed: ASC, DESC, ASCENDING, DESCENDING. Default: ASC'; END IF; RETURN QUERY EXECUTE format( 'SELECT id, name, clientname, count(*) OVER() AS full_count FROM design_list WHERE ($1 = $2 OR category ILIKE $1) ORDER BY %I %s LIMIT %s OFFSET %s' , _order_by, _order_asc_desc, _limit, _offset) USING _category, _empty; END $func$;
Core feature: use format() to safely and elegantly concatenate your query string. Related:
ASC / DESC (or ASCENDING / DESCENDING) are fixed key words. I added a manual check (IF ...) and later concatenate with a simple %s. That's one way to assert legal input. For convenience, I added an error message for unexpected input and a parameter default, so the function defaults to ASC if the last parameter is omitted in the call. Related:
Addressing Pavel's valid comment, I concatenate _limit and _offset directly, so the query is already planned with those parameters.
_limit and _offset are integer parameters, so we can use plain %s without the danger of SQL injection. You might want to assert reasonable values (exclude negative values and values too high) before concatenating ...
Other notes:
Use a consistent naming convention. I prefixed all parameters and variables with an underscore _, not just some.
Not using table qualification inside EXECUTE, since there is only a single table involved and the EXECUTE has its separate scope.
I renamed some parameters to clarify. _order_by instead of _sort_by; _order_asc_desc instead of _order.
USINGwhen you don't use$3and$4(sort_by and _order, as it happens)? You ORDER BYsort_by, don't use_orderat all, and say ordering is working and sorting isn't???