Insérer du texte avec des guillemets simples dans PostgreSQL

433

J'ai une table test(id,name).

Je dois insérer des valeurs comme: 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');

Je reçois une erreur si j'exécute l'une des instructions ci-dessus.

S'il existe une méthode pour le faire correctement, veuillez la partager. Je ne veux pas de déclarations préparées.

Est-il possible d'utiliser le mécanisme d'échappement sql?

MAHI
la source
1
Utilisez la valeur qui s'échappe de votre bibliothèque cliente. Pour plus d'informations, vous devrez indiquer comment vous accédez à la base de données.
Richard Huxton
La base de données @Richard Huxton est accessible par java.
MAHI
2
Utilisez donc les espaces réservés jdbc standard. Ou expliquez pourquoi ce n'est pas le meilleur choix.
Richard Huxton
@ Richard Huxton, je ne dis pas que ce n'est pas le meilleur choix, je cherche s'il existe une méthode d'échappement dans sql pour le faire.
MAHI
Eh bien, voir la réponse de @ Claudix ci-dessous, mais les littéraux de valeur auront évidemment besoin d'un échappement différent selon leur type postgresql.org/docs/current/static/datatype.html
Richard Huxton

Réponses:

765

Littéraux de chaîne

Échapper aux guillemets simples 'en les doublant -> ''est la méthode standard et fonctionne bien sûr:

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

Dans les anciennes versions ou si vous exécutez toujours avec standard_conforming_strings = offou, généralement, si vous ajoutez votre chaîne à Epour déclarer la syntaxe de chaîne d'échappement Posix , vous pouvez également vous échapper avec la barre oblique inverse \:

E'user\'s log'

La barre oblique inverse elle-même est échappée avec une autre barre oblique inverse. Mais ce n'est généralement pas préférable.
Si vous devez gérer de nombreux guillemets simples ou plusieurs couches d'échappement, vous pouvez éviter de citer l'enfer dans PostgreSQL avec des chaînes entre guillemets :

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

Pour éviter davantage la confusion entre les cotations en dollars, ajoutez un jeton unique à chaque paire:

$token$escape ' with ''$token$

Qui peuvent être imbriqués dans n'importe quel nombre de niveaux:

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

Faites attention si le $personnage doit avoir une signification particulière dans votre logiciel client. Vous devrez peut-être y échapper en plus. Ce n'est pas le cas avec les clients PostgreSQL standard comme psql ou pgAdmin.

Tout cela est très utile pour écrire des fonctions plpgsql ou des commandes SQL ad hoc. Cependant, cela ne peut pas réduire le besoin d'utiliser des instructions préparées ou une autre méthode pour se protéger contre l'injection SQL dans votre application lorsque l'entrée utilisateur est possible. @ La réponse de Craig en contient plus. Plus de détails:

Valeurs à l'intérieur de Postgres

Lorsque vous traitez des valeurs à l'intérieur de la base de données, il existe quelques fonctions utiles pour citer correctement les chaînes:

  • quote_literal()ouquote_nullable() - ce dernier sort la chaîne NULLpour une entrée nulle. (Il y a aussi quote_ident()de guillemets doubles cordes si nécessaire pour obtenir valides SQL identifiants .)
  • format()avec le spécificateur de format %Lest équivalent à quote_nullable().
    Comme:format('%L', string_var)
  • concat()ou neconcat_ws() sont généralement pas bons car ceux-ci n'échappent pas aux guillemets simples et aux barres obliques inverses imbriqués.
Erwin Brandstetter
la source
1
Il convient également de noter que certaines versions de PgJDBC ont des problèmes avec les guillemets en dollars - en particulier, il peut ne pas ignorer les terminateurs d'instructions (;) dans les chaînes entre guillemets.
Craig Ringer
1
Cette réponse associée contient des détails sur le problème avec JDBC.
Erwin Brandstetter
1
Et si vous voulez échapper à s'tring de la colonne de texte lors de l'insertion en cas de langage procédural, etc., vous pouvez utiliser la fonction de chaîne quote_literal (nom_colonne).
alexglue le
1
$ token $ est génial. Merci.
mythicalcoder
@ErwinBrandstetter, re "peut être imbriqué n'importe quel nombre de niveaux": mais SELECT $outer$OUT$inner$INNER$inner$ER$outer$;prouve que l'imbrication de 2ème niveau ne fonctionne pas ici.?
filiprem
46

C'est tellement de mondes mauvais, car votre question implique que vous avez probablement des trous d' injection SQL béants dans votre application.

Vous devez utiliser des instructions paramétrées. Pour Java, utilisez PreparedStatementavec des espaces réservés . Vous dites que vous ne voulez pas utiliser des instructions paramétrées, mais vous n'expliquez pas pourquoi , et franchement, cela doit être une très bonne raison de ne pas les utiliser car elles sont le moyen le plus simple et le plus sûr de résoudre le problème que vous essayez résoudre.

Voir Prévention de l'injection SQL en Java . Ne soyez pas la prochaine victime de Bobby .

Il n'y a pas de fonction publique dans PgJDBC pour la citation de chaîne et l'échappement. C'est en partie parce que cela pourrait donner l'impression que c'est une bonne idée.

Il existe des fonctions de citation intégrées quote_literalet quote_identdans PostgreSQL, mais elles sont destinées aux PL/PgSQLfonctions qui utilisent EXECUTE. Ces jours-ci quote_literalsont principalement obsolètes par EXECUTE ... USING, qui est la version paramétrée , car elle est plus sûre et plus facile . Vous ne pouvez pas les utiliser aux fins que vous expliquez ici, car ce sont des fonctions côté serveur.


Imaginez ce qui se passe si vous obtenez la valeur ');DROP SCHEMA public;--d'un utilisateur malveillant. Vous produiriez:

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

qui se décompose en deux déclarations et un commentaire qui est ignoré:

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

Oups, voilà votre base de données.

Craig Ringer
la source
J'aurais tendance à être d'accord avec une exception - les clauses "où" (bien qu'il dise "insérer") avec une liste de valeurs dans le cadre d'une clause "in" (ou un groupe de "ou" s). Je suppose que vous pourriez compter la taille de la liste et générer le texte de l'instruction préparée avec une clause "in", mais cela devient bizarre dans ce cas d'utilisation.
Roboprog
@Roboprog Avec certains pilotes clients, vous pouvez utiliser = ANY(?)un paramètre de tableau.
Craig Ringer
12
J'ai souvent utilisé des insertions littérales comme celle-ci pour démarrer les données, aux côtés de DDL. Essayons simplement de répondre à des questions plutôt qu'à des réponses comme «vous vous trompez»
ThatDataGuy
1
@ThatDataGuy juste commentaire, mais dans cette question, le PO a ajouté un commentaire disant database is accessed by javaque cela répond directement à la question. Il est également très important que les personnes venant ici soient sensibilisées aux dangers potentiels, d'autant plus que l'injection SQL est la première cause de vulnérabilité logicielle. Une fois conscients du problème, les gens peuvent prendre des décisions éclairées quant au moment où cela n'a pas d'importance, comme votre cas d'utilisation d'amorçage.
Davos
Exactement. Les gens copient et collent également beaucoup de code. Je cesserai d'avertir les gens à ce sujet le jour où j'arrêterai de voir les vulnérabilités d'injection SQL quotidiennement dans le code de production.
Craig Ringer
26

Selon la documentation PostgreSQL (4.1.2.1. Constantes de chaîne) :

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

Voir aussi le paramètre standard_conforming_strings , qui contrôle si l'échappement avec des barres obliques inverses fonctionne.

Claudix
la source
merci pour la réponse, mais je dois échapper manuellement chaque caractère en l'utilisant, s'il existe des fonctions intégrées pour le faire?
MAHI
3
@MAHI S'il y avait une telle fonction, ce serait dans PgJDBC, pas dans PostgreSQL lui-même, car l'échappement doit être fait côté client. Il n'y a pas une telle fonction publique documentée parce que c'est une idée terrible . Vous devez utiliser des instructions paramétrées afin de ne pas avoir à effectuer d'échappements potentiellement non fiables.
Craig Ringer
13

Dans postgresql si vous voulez insérer des valeurs avec, 'vous devez donner plus'

 insert into test values (1,'user''s log');
 insert into test values (2,'''my users''');
 insert into test values (3,'customer''s');
chasseur
la source
vote positif pour afficher les triples guillemets si vous avez une chaîne entre
guillemets
, car c'est une solution simple
ktaria
5

vous pouvez utiliser la fonction postrgesql chr (int):

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

Si vous devez faire le travail à l'intérieur de Pg:

to_json(value)

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

hatenine
la source
Comment cette question est-elle liée au JSON?
Erwin Brandstetter
1
@ErwinBrandstetter, désolé, je suis peut-être hors tension .. mais cela échappe aux citations dans les chaînes
hatenine
1
C'est une tout autre affaire. Vous pouvez utiliser format(), quote_literal()ou quote_nullable()pour échapper des guillemets. Voir: stackoverflow.com/a/25143945/939860
Erwin Brandstetter
0
select concat('''','abc','''')
Rushi The Sharma
la source
Cela nécessiterait de citer user's logà la place de abccorrectement pour commencer. Catch 22.
Erwin Brandstetter