Alguém me disse que usar entrada de texto para nomes de coluna e formatá-la, como faço abaixo, raramente é uma boa ideia. Quando perguntei por que, no entanto, uma resposta não foi dada. Isso foi no postgresql IRC, e esses caras parecem saber das coisas. Então eu gostaria de saber por que não é aconselhável? Estou principalmente me perguntando se isso abre a porta para injeção de sql.
create or replace function getItemsOrderBy(order_by_p text)
RETURNS TABLE (id int) AS $$
BEGIN
return query EXECUTE format('
SELECT id
FROM items
ORDER BY %s', order_by_p) ;
END;
Ele também disse para usar execute
com using
em vez disso, então qual é a diferença entre isso:
return query EXECUTE format('
SELECT id
FROM items
ORDER BY %s', order_by_p) ;
e isto :
return query EXECUTE '
SELECT id
FROM items
ORDER BY $1' USING order_by_p ;
Minha função é mais complexa do que a acima - a parte do formato é apenas parte dela. Eu tenho a opção de criar uma função que pode lidar com vários casos (para pedidos) ou criar vários deles para lidar com todos os pedidos. Eu senti que fazer apenas um era mais prático. Não ter nenhuma função não é uma opção.
Na verdade, estou usando pg-promise
, mas fiquei com a impressão de que, como estou fazendo muitas idas e vindas entre o back-end e o banco de dados (enviar algo, aguardar resposta, calcular outra coisa, enviar novamente ..) eu deveria ir com função e deixe tudo acontecer de uma vez.
Há duas perguntas aqui,
EXECUTE .. USING
eEXECUTE FORMAT()
A diferença entre
EXECUTE .. USING
eEXECUTE FORMAT()
Dos documentos,
Então você tem alguns argumentos aqui.
EXECUTE FORMAT() ... USING
USING
permite que o plano seja armazenado em cache.USING
permite que os símbolos permaneçam símbolos e impede que eles tenham que ser convertidos em texto e reescapados.USING
não pode ser usado com identificadores, apenas literais.Embrulhar e gerar instruções SQL simples em uma função procedural é uma má ideia.
Quanto à outra parte da questão,
Há muitas razões para isso,
TABLE (id int)
E não é SQL. Você está construindo uma nova linguagem em cima de um DBMS. Por quê?
Quanto ao componente dinâmico, existem outras maneiras de contornar o problema. Tomemos, por exemplo, a declaração exata fornecida, o pior cenário é onde você vê que
Você tem que escrever explicitamente o pedido. Por pior que seja, é uma solução melhor na minha opinião.
Um passo ainda a mais seria usar uma biblioteca que fornece algum tipo de assistência para gerar, como
pg-promise
Ou DBIx::Abstract ou um ORM como DBIx::Class .
Apenas abordando sua segunda pergunta sobre
EXECUTE
eUSING
:O elefante na sala: a alternativa sugerida é um completo absurdo por vários motivos.
A
USING
cláusula ofEXECUTE
é usada para passar valores . Nem literais, nem identificadores, nem outros elementos de sintaxe, apenas valores. Os mesmos valores que você pode passar para instruções preparadas porque, internamente, é exatamente isso que acontece. A instrução é preparada e executada usando valores fornecidos porUSING
.Isso é um absurdo múltiplo:
ORDER BY
não faz sentido com um valor constante, seria apenas ruído.order_by_p
deve ser um nome de coluna, que deve ser interpretado como identificador, não como valor de dados. Você nunca usaUSING
para isso. Veja acima.format()
o uso do%I
especificador ou comquote_ident()
.Funcionaria assim:
Contanto que você não aninhe essa função em consultas externas (além de
SELECT * FROM my_func()
), e não precise otimizar o desempenho (como para muitas chamadas repetidas na mesma sessão), não há nada de errado nisso.Observe que os identificadores diferenciam maiúsculas de minúsculas e você deve fornecer a ortografia correta. Respostas relacionadas: