Isso pode ser ainda mais simplificado e melhorado:
CREATE OR REPLACE FUNCTION some_f(_tbl regclass, OUT result integer)
LANGUAGE plpgsql AS
$func$
BEGIN
EXECUTE format('SELECT (EXISTS (SELECT FROM %s WHERE id = 1))::int', _tbl)
INTO result;
END
$func$;
Chamada com nome qualificado pelo esquema (veja abaixo):
SELECT some_f('myschema.mytable'); -- would fail with quote_ident()
Ou:
SELECT some_f('"my very uncommon table name"');
Pontos principais
Use um
OUT
parâmetro para simplificar a função. Você pode selecionar diretamente o resultado do SQL dinâmico e pronto. Não há necessidade de variáveis e códigos adicionais. EXISTS
faz exatamente o que você quer. Você obtém true
se a linha existir ou false
por outro lado. Existem várias maneiras de fazer isso, EXISTS
normalmente é mais eficiente. Você parece querer um inteiro de volta, então eu converto o
boolean
resultado de EXISTS
para integer
, que produz exatamente o que você tinha. Eu retornaria boolean em vez de. Eu uso o tipo de identificador de objeto
regclass
como tipo de entrada para _tbl
. Isso faz tudo quote_ident(_tbl)
ou format('%I', _tbl)
faria, mas melhor, porque:-
.. impede a injeção de SQL tão bem.
-
.. ele falha imediatamente e com mais graça se o nome da tabela for inválido / não existir / for invisível para o usuário atual. (Umaregclass
parâmetro só é aplicável para existente tabelas.)
-
.. ele funciona com nomes de tabela qualificados pelo esquema, onde um simplesquote_ident(_tbl)
ouformat(%I)
falharia porque eles não podem resolver a ambiguidade. Você teria que passar e escapar nomes de esquema e tabela separadamente.
Funciona apenas para existentes mesas, obviamente.
Eu ainda uso
format()
, porque simplifica a sintaxe (e para demonstrar como é usada), mas com %s
em vez de %I
. Normalmente, as consultas são mais complexas, então format()
ajuda mais. Para o exemplo simples, poderíamos apenas concatenar:EXECUTE 'SELECT (EXISTS (SELECT FROM ' || _tbl || ' WHERE id = 1))::int'
Não há necessidade de qualificar a tabela
id
coluna enquanto houver apenas uma única tabela no FROM
Lista. Nenhuma ambiguidade é possível neste exemplo. Comandos SQL (dinâmicos) dentro de EXECUTE
ter um escopo separado , variáveis ou parâmetros de função não são visíveis lá - ao contrário de comandos SQL simples no corpo da função. É por isso que você sempre escape da entrada do usuário para SQL dinâmico corretamente:
db<>mexa aqui demonstrando injeção de SQL
antigo sqlfiddle