Sua função pode ficar assim:
CREATE FUNCTION select_transactions3(_col text, _val text, _limit int)
RETURNS SETOF transactions AS
$BODY$
BEGIN
RETURN QUERY EXECUTE '
SELECT *
FROM transactions
WHERE ' || quote_ident(_col) || ' = $1
LIMIT $2'
USING _val, _limit;
END;
$BODY$
LANGUAGE plpgsql VOLATILE SECURITY DEFINER;
NO PostgreSQL 9.1 ou posterior, é mais simples com o formato
format()
...
RETURN QUERY EXECUTE format('
SELECT *
FROM transactions
WHERE %I = $1
LIMIT $2', _col)
USING _val, _limit;
...
%I escapa de identificadores como quote_ident() . Pontos principais:
-
Você estava se deparando com a limitação do SQL dinâmico que não pode usar parâmetros para identificadores. Você tem que construir a string de consulta com o nome da coluna e depois execute.
-
Você pode fazer isso com valores embora. Demonstro o uso doUSINGcláusula paraEXECUTE. Observe também o uso dequote_ident():evita a injeção de SQL e certos erros de sintaxe.
-
Eu também simplifiquei amplamente sua função.[RETURN QUERY EXECUTE][3]torna seu código mais curto e mais rápido. Não há necessidade de fazer um loop se tudo o que você faz é retornar a linha.
-
Eu uso nomeadoINparâmetros, para que você não fique confuso com a notação $ na string de consulta.$1e$2dentro da string de consulta referem-se aos valores fornecidos noUSINGcláusula, não aos parâmetros de entrada.
-
Eu mudo paraSELECT *pois você precisa retornar a linha inteira para corresponder ao tipo de retorno declarado de qualquer maneira.
-
Por último, mas não menos importante:Certifique-se de considerar o que o manual tem a dizer sobre as funções declaradasSECURITY DEFINER.
TIPO DE DEVOLUÇÃO
Se você não quiser retornar a linha inteira, uma possibilidade conveniente é:
CREATE FUNCTION select_transactions3(_col text, _val text, _limit int)
RETURNS TABLE (invoice_no varchar(125), amount numeric(12,2) AS ...
Então você não precisa fornecer uma lista de definição de coluna com cada chamada e pode simplificar para:
SELECT * FROM select_to_transactions3('invoice_no', '1103300105472', 1);