Inserir texto com aspas simples no PostgreSQL

432

Eu tenho uma tabela test(id,name).

Eu preciso inserir valores como: user's log, 'my user', customer's.

 insert into test values (1,'user's log');
 insert into test values (2,''my users'');
 insert into test values (3,'customer's');

Estou recebendo um erro se executar alguma das instruções acima.

Se houver algum método para fazer isso corretamente, compartilhe. Não quero nenhuma declaração preparada.

É possível usar o mecanismo de escape do sql?

MAHI
fonte
1
Use o valor que a biblioteca de clientes fornecer. Para mais informações, você terá que dizer como está acessando o banco de dados.
Richard Huxton
O banco de dados @Richard Huxton é acessado por java.
MAHI 07/09/12
2
Portanto, use os espaços reservados padrão do jdbc. Ou explique por que essa não é a melhor escolha.
Richard Huxton
@ Richard Huxton eu não estou dizendo que não é a melhor escolha, estou pesquisando se existe algum método de escape no sql para fazer isso.
MAHI
Bem, ver @ resposta de Claudix abaixo, mas, obviamente, literais valor precisará diferente escapar dependendo do seu tipo postgresql.org/docs/current/static/datatype.html
Richard Huxton

Respostas:

763

Literais de string

Escapar aspas simples 'dobrando-as -> ''é a maneira padrão e funciona, é claro:

'user's log'     -- incorrect syntax (unbalanced quote)
'user''s log'

Nas versões antigas, ou se você ainda executa com standard_conforming_strings = off, ou, geralmente, se você anexa sua sequência Epara declarar a sintaxe da sequência de escape Posix , também é possível escapar com a barra invertida \:

E'user\'s log'

A barra invertida em si é escapada com outra barra invertida. Mas isso geralmente não é preferível.
Se você precisar lidar com muitas aspas simples ou múltiplas camadas de escape, evite citar o inferno no PostgreSQL com cadeias de caracteres cotadas em dólar :

'escape '' with '''''
$$escape ' with ''$$

Para evitar ainda mais confusão entre cotações em dólar, adicione um token exclusivo para cada par:

$token$escape ' with ''$token$

Que pode ser aninhado em qualquer número de níveis:

$token2$Inner string: $token1$escape ' with ''$token1$ is nested$token2$

Preste atenção se o $personagem tiver um significado especial no software cliente. Você pode ter que escapar disso também. Este não é o caso de clientes padrão do PostgreSQL como psql ou pgAdmin.

Isso é muito útil para escrever funções plpgsql ou comandos SQL ad-hoc. Ele não pode aliviar a necessidade de usar instruções preparadas ou algum outro método para proteger contra injeção de SQL em seu aplicativo quando a entrada do usuário é possível. A resposta de Craig tem mais sobre isso. Mais detalhes:

Valores dentro do Postgres

Ao lidar com valores dentro do banco de dados, existem algumas funções úteis para citar seqüências de caracteres corretamente:

  • quote_literal()ouquote_nullable() - o último gera a string NULLpara entrada nula. (Há também quote_ident()a aspas duplas cordas quando necessário para obter SQL válidas identificadores .)
  • format()com o especificador de formato %Lé equivalente a quote_nullable().
    Gostar:format('%L', string_var)
  • concat()ouconcat_ws() normalmente não são boas, pois elas não escapam de aspas simples e barras invertidas.
Erwin Brandstetter
fonte
1
Também vale a pena notar que algumas versões do PgJDBC têm problemas com a cotação do dólar - em particular, pode falhar em ignorar os terminadores de instruções (;) dentro de cadeias de caracteres cotadas em dólar.
Craig campainha
1
Esta resposta relacionada possui detalhes para o problema com o JDBC.
precisa saber é o seguinte
1
E se você quiser escapar da string de texto na inserção no caso de linguagem processual, etc, poderá usar a função de string quote_literal (column_name).
Alexlue
1
$ token $ é incrível. Obrigado.
Mythicalcoder
@ErwinBrandstetter, re "pode ​​ser aninhado em qualquer número de níveis": mas SELECT $outer$OUT$inner$INNER$inner$ER$outer$;prova que o aninhamento de segundo nível não funciona aqui.
Filprem 12/04/19
46

São muitos mundos ruins, porque sua pergunta implica que você provavelmente tem buracos de injeção de SQL em seu aplicativo.

Você deve estar usando instruções parametrizadas. Para Java, use PreparedStatementcom espaços reservados . Você diz que não deseja usar instruções parametrizadas, mas não explica o motivo e, francamente, deve ser uma boa razão para não usá-las, porque elas são a maneira mais simples e segura de corrigir o problema que você está tentando resolver.

Consulte Prevenindo a injeção de SQL em Java . Não seja a próxima vítima de Bobby .

Não há função pública no PgJDBC para citação e escape de string. Em parte porque isso pode parecer uma boa ideia.

Não são built-in citando funções quote_literale quote_identno PostgreSQL, mas eles são para PL/PgSQLfunções que usam EXECUTE. Hoje em dia a quote_literalmaioria é obsoleta por EXECUTE ... USING, que é a versão parametrizada , porque é mais segura e fácil . Você não pode usá-los para a finalidade que você explica aqui, porque são funções do lado do servidor.


Imagine o que acontece se você obtiver o valor ');DROP SCHEMA public;--de um usuário mal-intencionado. Você produziria:

insert into test values (1,'');DROP SCHEMA public;--');

que divide em duas declarações e um comentário que é ignorado:

insert into test values (1,'');
DROP SCHEMA public;
--');

Opa, lá vai o seu banco de dados.

Craig Ringer
fonte
Eu tenderia a concordar com uma exceção - cláusulas "where" (embora ele diga "insert") com uma lista de valores como parte de uma cláusula "in" (ou um monte de "ou" s). Suponho que você possa contar o tamanho da lista e gerar o texto para a instrução preparada com uma cláusula "in", mas fica estranho nesse caso de uso.
Roboprog 27/10/2014
@ Roboprog Com alguns drivers de cliente, você pode usar = ANY(?)e um parâmetro de matriz.
Craig Ringer
12
Eu sempre usei inserções literais como esta para inicializar dados, juntamente com DDL. Vamos apenas tentar responder a perguntas do que respostas como 'você está fazendo errado'
ThatDataGuy 29/11
1
@ThatDataGuy comentário justo, mas nesta questão o OP adicionou um comentário dizendo database is accessed by javaque isso aborda diretamente a questão. Também é muito importante que as pessoas que vêm aqui estejam cientes dos perigos em potencial, especialmente porque o SQL Injection é a principal causa de vulnerabilidade de software. Uma vez cientes do problema, as pessoas podem tomar decisões informadas sobre quando isso não importa, como seu caso de uso de inicialização.
Davos
Exatamente. As pessoas também copiam e colam muito o código. Pararei de alertar as pessoas sobre isso no dia em que parar de ver diariamente as vulnerabilidades de injeção de SQL no código de produção.
Craig Ringer
26

De acordo com a documentação do PostgreSQL (4.1.2.1. String Constants) :

 To include a single-quote character within a string constant, write two 
 adjacent single quotes, e.g. 'Dianne''s horse'.

Consulte também o parâmetro standard_conforming_strings , que controla se o escape com barras invertidas funciona.

Claudix
fonte
obrigado pela resposta, mas eu tenho que escapar manualmente cada caractere usando isso, se houver algum construído em funções para fazer isso?
MAHI 7/09/12
3
@MAHI Se houvesse essa função, ela seria no PgJDBC, não no PostgreSQL, porque a fuga deve ser feita no lado do cliente. Não existe essa função pública documentada porque é uma péssima ideia. Você deve usar instruções parametrizadas para não precisar fazer nenhum tipo de escape potencialmente não confiável.
Craig Ringer
13

No postgresql, se você deseja inserir valores 'nele, então, para isso, você deve fornecer'

 insert into test values (1,'user''s log');
 insert into test values (2,'''my users''');
 insert into test values (3,'customer''s');
Caçador
fonte
voto positivo por mostrar as aspas triplas se você tiver uma string entre aspas
winkbrace 17/03
, como é uma solução simples
ktaria 12/04
5

você pode usar a função postrgesql chr (int):

insert into test values (2,'|| chr(39)||'my users'||chr(39)||');
Slava Struminski
fonte
2

Se você precisar fazer o trabalho na página:

to_json(value)

https://www.postgresql.org/docs/9.3/static/functions-json.html#FUNCTIONS-JSON-TABLE

hatenina
fonte
Como esta questão está relacionada ao JSON?
Erwin Brandstetter
1
@ErwinBrandstetter, desculpe, eu poderia estar fora .. mas ele escapa citações em cordas
hatenine
1
Isso é outra questão. Que você pode usar format(), quote_literal()ou quote_nullable()para escapar aspas. Veja: stackoverflow.com/a/25143945/939860
Erwin Brandstetter
0
select concat('''','abc','''')
Rushi The Sharma
fonte
Isso exigiria uma citação user's logno lugar de forma abcadequada para começar. Captura 22.
Erwin Brandstetter