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 doUSING
clá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 nomeadoIN
parâmetros, para que você não fique confuso com a notação $ na string de consulta.$1
e$2
dentro da string de consulta referem-se aos valores fornecidos noUSING
clá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);