Insertar texto con comillas simples en PostgreSQL

433

Tengo una tabla test(id,name).

Necesito insertar 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');

Recibo un error si ejecuto alguna de las declaraciones anteriores.

Si hay algún método para hacerlo correctamente, compártelo. No quiero ninguna declaración preparada.

¿Es posible usar el mecanismo de escape sql?

MAHI
fuente
1
Use cualquier valor que se le escape a su biblioteca cliente. Para obtener más información, tendrá que decir cómo está accediendo a la base de datos.
Richard Huxton
Java accede a la base de datos de Richard Huxton.
MAHI
2
Por lo tanto, use los marcadores de posición jdbc estándar. O explique por qué esa no es la mejor opción.
Richard Huxton
@ Richard Huxton No estoy diciendo que esa no sea la mejor opción, estoy buscando si existe algún método de escape en SQL para hacerlo.
MAHI
Bueno, vea la respuesta de @ Claudix a continuación, pero obviamente los literales de valores necesitarán un escape diferente dependiendo de su tipo postgresql.org/docs/current/static/datatype.html
Richard Huxton

Respuestas:

764

Literales de cadena

Escapando de comillas simples 'duplicándolas ->'' es la forma estándar y funciona, por supuesto:

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

En versiones anteriores o si todavía se ejecuta con standard_conforming_strings = offo, en general, si antepone su cadena Epara declarar la sintaxis de cadena de escape de Posix , también puede escapar con la barra invertida\ :

E'user\'s log'

La barra diagonal inversa se escapa con otra barra diagonal inversa. Pero eso generalmente no es preferible.
Si tiene que lidiar con muchas comillas simples o múltiples capas de escape, puede evitar citar el infierno en PostgreSQL con cadenas de dólares :

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

Para evitar aún más la confusión entre las cotizaciones en dólares, agregue un token único a cada par:

$token$escape ' with ''$token$

Que se puede anidar cualquier cantidad de niveles:

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

Presta atención si el $ personaje debe tener un significado especial en el software de tu cliente. Es posible que tenga que escapar además. Este no es el caso con clientes estándar de PostgreSQL como psql o pgAdmin.

Todo eso es muy útil para escribir funciones plpgsql o comandos SQL ad-hoc. Sin embargo, no puede aliviar la necesidad de usar declaraciones preparadas o algún otro método para proteger contra la inyección de SQL en su aplicación cuando es posible la entrada del usuario. La respuesta de @ Craig tiene más información al respecto. Más detalles:

Valores dentro de Postgres

Al tratar con valores dentro de la base de datos, hay un par de funciones útiles para citar cadenas correctamente:

  • quote_literal()oquote_nullable() - este último genera la cadena NULLpara una entrada nula. (También hay quote_ident()que comillas dobles cadenas donde sea necesario para obtener SQL válidas identificadores .)
  • format()con el especificador de formato %Les equivalente a quote_nullable().
    Me gusta:format('%L', string_var)
  • concat()oconcat_ws() normalmente no son buenas, ya que no escapan a comillas simples anidadas y barras diagonales inversas.
Erwin Brandstetter
fuente
1
También vale la pena señalar que algunas versiones de PgJDBC tienen problemas con las cotizaciones en dólares, en particular, puede fallar en ignorar los terminadores de declaraciones (;) dentro de las cadenas con cotizaciones en dólares.
Craig Ringer
1
Esta respuesta relacionada tiene detalles para el problema con JDBC.
Erwin Brandstetter
1
Y si desea escapar de s'tring de la columna de texto en la inserción en caso de lenguaje de procedimiento, etc., puede usar la función de cadena quote_literal (column_name).
alexglue
1
$ token $ es increíble. Gracias.
mythicalcoder
@ErwinBrandstetter, re "se puede anidar cualquier número de niveles": pero SELECT $outer$OUT$inner$INNER$inner$ER$outer$;demuestra que el anidamiento de segundo nivel no funciona aquí.
filiprem
46

Esto es tan malo, porque su pregunta implica que probablemente tenga una gran inyección de SQL agujeros de en su aplicación.

Debería estar usando declaraciones parametrizadas. Para Java, use PreparedStatementcon marcadores de posición . Dices que no quieres usar declaraciones parametrizadas, pero no explicas por qué , y francamente tiene que ser una muy buena razón para no usarlas porque son la forma más simple y segura de solucionar el problema que estás intentando. resolver.

Consulte Prevención de la inyección de SQL en Java . No seas bobby la próxima víctima de .

No hay una función pública en PgJDBC para las citas y escapes de cadenas. Eso se debe en parte a que podría parecer una buena idea.

No son incorporados al citar las funciones quote_literaly quote_identen PostgreSQL, pero son para PL/PgSQLlas funciones que utilizan EXECUTE. En estos días quote_literalestá obsoleto en su mayoría EXECUTE ... USING, que es la versión parametrizada , porque es más segura y fácil . No puede usarlos para el propósito que explica aquí, porque son funciones del lado del servidor.


Imagine lo que sucede si obtiene el valor ');DROP SCHEMA public;--de un usuario malintencionado. Producirías:

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

que se divide en dos declaraciones y un comentario que se ignora:

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

Vaya, ahí va tu base de datos.

Craig Ringer
fuente
Tendería a estar de acuerdo con una excepción: las cláusulas "where" (aunque él dice "insert") con una lista de valores como parte de una cláusula "in" (o un grupo de "o" s). Supongo que podría contar el tamaño de la lista y generar el texto de la declaración preparada con una cláusula "in", pero se vuelve extraño en ese caso de uso.
Roboprog
@Roboprog Con algunos controladores de cliente que puede usar = ANY(?)y un parámetro de matriz.
Craig Ringer
12
A menudo he usado inserciones literales como esta para arrancar datos, junto con DDL. Solo tratemos de responder preguntas que respuestas como "lo estás haciendo mal"
ThatDataGuy
1
@ThatDataGuy comentario justo, pero en esta pregunta el OP agregó un comentario que dice database is accessed by javaque esto aborda directamente la pregunta. También es muy importante que las personas que vienen aquí sean conscientes de los peligros potenciales, especialmente dado que la inyección SQL es la causa número 1 de vulnerabilidad de software. Una vez conscientes del problema, las personas pueden tomar decisiones informadas sobre cuándo no importa, como su caso de uso de arranque.
Davos
Exactamente. La gente también copia y pega mucho código. Dejaré de advertir a la gente sobre esto el día que deje de ver vulnerabilidades de inyección SQL diariamente en el código de producción.
Craig Ringer
26

De acuerdo con la documentación de PostgreSQL (4.1.2.1. Constantes de cadena) :

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

Vea también el parámetro standard_conforming_strings , que controla si funciona el escape con barras invertidas.

Claudix
fuente
gracias por responder, pero tengo que escapar manualmente de cada char usando este, si existe alguna función integrada para hacer esto.
MAHI
3
@MAHI Si hubiera tal función, sería en PgJDBC, no en PostgreSQL, porque el escape debe hacerse en el lado del cliente. No existe tal función pública documentada porque es una idea terrible . Debería usar declaraciones parametrizadas para no tener que hacer ningún tipo de escape potencialmente poco confiable.
Craig Ringer
13

En postgresql si desea insertar valores con 'él, entonces para esto debe dar más'

 insert into test values (1,'user''s log');
 insert into test values (2,'''my users''');
 insert into test values (3,'customer''s');
Cazador
fuente
voto a favor por mostrar las comillas triples si tiene una cadena citada
winkbrace
arriba, ya que es una solución simple
ktaria
5

puede usar la función postrgesql chr (int):

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

Si necesita hacer el trabajo dentro de Pg:

to_json(value)

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

hatenina
fuente
¿Cómo se relaciona esta pregunta con JSON?
Erwin Brandstetter
1
@ErwinBrandstetter, lo siento, podría estar fuera ... pero se escapa de las comillas
hatenine
1
Esa es otra cuestión por completo. Puede usar format(), quote_literal()o quote_nullable()para escapar de las comillas. Ver: stackoverflow.com/a/25143945/939860
Erwin Brandstetter
0
select concat('''','abc','''')
Rushi The Sharma
fuente
Para comenzar, esto requeriría cotizar user's logen lugar de abccorrectamente. Captura 22.
Erwin Brandstetter