715

I have a table test(id,name).

I need to insert values like: 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');

I am getting an error if I run any of the above statements.

If there is any method to do this correctly please share. I don't want any prepared statements.

Is it possible using sql escaping mechanism?

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
MAHI
  • 9,263
  • 11
  • 36
  • 47
  • 1
    Use whatever value escaping your client library provides. For more information you'll have to say how you're accessing the database. – Richard Huxton Sep 07 '12 at 11:13
  • @Richard Huxton database is accessed by java. – MAHI Sep 07 '12 at 11:16
  • 2
    So use the standard jdbc placeholders. Or explain why that's not the best choice. – Richard Huxton Sep 07 '12 at 11:23
  • 1
    @Richard Huxton i am not saying that's not best choice, i am searching if their exists any escaping method in sql to do so. – MAHI Sep 07 '12 at 11:26
  • Well, see @Claudix's reply below, but obviously value literals will need different escaping depending on their type http://www.postgresql.org/docs/current/static/datatype.html – Richard Huxton Sep 07 '12 at 11:30

8 Answers8

1220

String literals

Escaping single quotes ' by doubling them up → '' is the standard way and works of course:

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

Plain single quotes (ASCII / UTF-8 code 39), mind you, not backticks `, which have no special purpose in Postgres (unlike certain other RDBMS) and not double-quotes ", used for identifiers.

In old versions or if you still run with standard_conforming_strings = off or, generally, if you prepend your string with E to declare Posix escape string syntax, you can also escape with the backslash \:

E'user\'s log'

Backslash itself is escaped with another backslash. But that's generally not preferable.
If you have to deal with many single quotes or multiple layers of escaping, you can avoid quoting hell in PostgreSQL with dollar-quoted strings:

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

To further avoid confusion among dollar-quotes, add a unique token to each pair:

$token$escape ' with ''$token$

Which can be nested any number of levels:

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

Pay attention if the $ character should have special meaning in your client software. You may have to escape it in addition. This is not the case with standard PostgreSQL clients like psql or pgAdmin.

That is all very useful for writing PL/pgSQL functions or ad-hoc SQL commands. It cannot alleviate the need to use prepared statements or some other method to safeguard against SQL injection in your application when user input is possible, though. @Craig's answer has more on that. More details:

Values inside Postgres

When dealing with values inside the database, there are a couple of useful functions to quote strings properly:

  • quote_literal() or quote_nullable() - the latter outputs the unquoted string NULL for null input.
    There is also quote_ident() to double-quote strings where needed to get valid SQL identifiers.
  • format() with the format specifier %L is equivalent to quote_nullable().
    Like: format('%L', string_var)
  • concat() or concat_ws() are typically no good for this purpose as those do not escape nested single quotes and backslashes.
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
  • 2
    It's also worth noting that some PgJDBC versions have issues with dollar-quoting - in particular, it may fail to ignore statement-terminators (;) within dollar-quoted strings. – Craig Ringer Apr 07 '14 at 08:10
  • 1
    [This related answer](http://stackoverflow.com/questions/22747225/exceptions-when-creating-a-trigger-in-postgresql-9-1/22748778#22748778) has details for the problem with JDBC. – Erwin Brandstetter Apr 07 '14 at 12:56
  • 1
    And if u want to escape s'tring from text column on insertion in case of procedural language etc, then you can use quote_literal(column_name) string function. – alexglue Apr 09 '14 at 09:59
  • @ErwinBrandstetter, re "can be nested any number of levels": but `SELECT $outer$OUT$inner$INNER$inner$ER$outer$;` proves that 2nd level nesting does not work here.? – filiprem Apr 12 '19 at 13:12
  • @filiprem: What makes you think it wouldn't work? Consider this [db<>fiddle](https://dbfiddle.uk/?rdbms=postgres_11&fiddle=b2898433faa74422c9bac4635b04ef72) demonstrating 3 levels of nesting. – Erwin Brandstetter Apr 16 '19 at 12:08
  • @ErwinBrandstetter, when I run `SELECT $outer$_A_$inner$_B_$inner$_C_$outer$`, I get `_A_$inner$_B_$inner$_C_` not `_A__B__C_`. – filiprem Apr 16 '19 at 22:03
  • @filiprem: But that's exactly what you're supposed to get. – Erwin Brandstetter Apr 16 '19 at 22:48
  • 1
    We've noticed that attempting to insert a string literal to Redshift (which is effectively old Postgres 8.x) matches what you said here about old versions. In that platform if a string literal has a backslash which doesn't combine to a valid escape sequence with the following character, that backslash just disappears when inserted, so doubling the backslashes works in that case too. You can easily get stung when loading xml/html literal values. Literal used to mean (and still does in Redshift) literal _with escaping_. Nice that you have to explicitly request that behaviour now with `E`. – Davos May 03 '19 at 01:26
  • Erwin, this is the logical correct answer, but could you please prefix it with a warning about the dangers of SQL-Injection? Also include a link to https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html for more information. – Kind Contributor Sep 23 '20 at 03:18
  • @Todd: You may have missed my warning against SQLi, links and techniques to quote properly. – Erwin Brandstetter Sep 23 '20 at 10:55
  • how can i apply this when working with a variable that contains single quote and not plain text ? – Youssef Boudaya May 25 '21 at 13:46
63

According to PostgreSQL documentation (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'.

See also the standard_conforming_strings parameter, which controls whether escaping with backslashes works.

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
Claudi
  • 5,224
  • 17
  • 30
  • thanks for reply, but i have to manually escape each char by using this, if their exists any built in functions for doing this ? – MAHI Sep 07 '12 at 11:27
  • 3
    @MAHI If there were such a function, it would be in PgJDBC, not in PostgreSQL its self, because the escaping must be done on the client-side. There is no such documented public function because it's a *terrible* idea. You should be using parameterised statements so you don't need to do any kind of potentially unreliable escaping. – Craig Ringer Sep 07 '12 at 11:47
55

This is so many worlds of bad, because your question implies that you probably have gaping SQL injection holes in your application.

You should be using parameterized statements. For Java, use PreparedStatement with placeholders. You say you don't want to use parameterised statements, but you don't explain why, and frankly it has to be a very good reason not to use them because they're the simplest, safest way to fix the problem you are trying to solve.

See Preventing SQL Injection in Java. Don't be Bobby's next victim.

There is no public function in PgJDBC for string quoting and escaping. That's partly because it might make it seem like a good idea.

There are built-in quoting functions quote_literal and quote_ident in PostgreSQL, but they are for PL/PgSQL functions that use EXECUTE. These days quote_literal is mostly obsoleted by EXECUTE ... USING, which is the parameterised version, because it's safer and easier. You cannot use them for the purpose you explain here, because they're server-side functions.


Imagine what happens if you get the value ');DROP SCHEMA public;-- from a malicious user. You'd produce:

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

which breaks down to two statements and a comment that gets ignored:

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

Whoops, there goes your database.

Craig Ringer
  • 307,061
  • 76
  • 688
  • 778
  • I would tend to agree with one exception - "where" clauses (although he says "insert") with a list of values as part of an "in" clause (or a bunch of "or"s). I suppose you could count the size of the list and generate the text to the prepared statement with an "in" clause, but it gets weird in that use case. – Roboprog Oct 27 '14 at 20:14
  • @Roboprog With some client drivers you can use `= ANY(?)` and an array parameter. – Craig Ringer Oct 27 '14 at 20:24
  • 30
    I've often used literal inserts like this to bootstrap data, alongside DDL. Lets just try to answer questions than responses like 'ýou're doing it wrong' – ThatDataGuy Nov 29 '18 at 11:05
  • 10
    @ThatDataGuy fair comment, but in this question the OP added a comment saying `database is accessed by java` so this does directly address the question. It is also very important for people coming here to be made aware of the potential dangers, especially given SQL Injection is the #1 cause of software vulnerability. Once aware of the problem, people can make informed decisions as to when it doesn't matter, like your bootstrapping use-case. – Davos May 03 '19 at 01:45
  • 7
    Exactly. People also copy&paste code a lot. I'll stop warning people about this the day I stop seeing SQL injection vulnerabilities daily in production code. – Craig Ringer May 06 '19 at 04:11
  • Thanks a lot for `quote_ident` IT DOES EXACTLY WHAT I NEEDED! Especially useful when one of your columns in the database has a name of one of registred Postgresql keyword – andilabs Jan 28 '20 at 16:24
  • 3
    This question & answer should be part of any incoming software developer's exams. OP operations "MAY BE SAFE", but it's really good to keep reminding even the most experienced people of the dangers. – Raymond Naseef Feb 07 '20 at 19:00
  • 3
    @Davos Agreed, people should be warned, but I don't think there should be a standalone warning-answer. It would be best to warn people with the following process: i) Write a comment on each answer asking the author to prefix their answer with a warning; ii) If the author doesn't update within 7 days, then you go in there and edit the answer with the preamble. – Kind Contributor Sep 23 '20 at 03:15
  • @Todd I don't see this answer as just a warning. A solution is provided: " use PreparedStatement with placeholders". Use "the parameterised version, because it's safer and easier." Your suggestion about making every answer prefix with the warning is a great idea. It would guarantee more people get the message. – Davos Sep 25 '20 at 03:30
40

In postgresql if you want to insert values with ' in it then for this you have to give extra '

 insert into test values (1,'user''s log');
 insert into test values (2,'''my users''');
 insert into test values (3,'customer''s');
Hunter
  • 820
  • 8
  • 19
8

you can use the postrgesql chr(int) function:

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

If you need to get the work done inside Pg:

to_json(value)

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

hatenine
  • 506
  • 3
  • 13
1

When I used Python to insert values into PostgreSQL, I also met the question: column "xxx" does not exist.

The I find the reason in wiki.postgresql:

PostgreSQL uses only single quotes for this (i.e. WHERE name = 'John'). Double quotes are used to quote system identifiers; field names, table names, etc. (i.e. WHERE "last name" = 'Smith').
MySQL uses ` (accent mark or backtick) to quote system identifiers, which is decidedly non-standard.

It means PostgreSQL can use only single quote for field names, table names, etc. So you can not use single quote in value.

My situation is: I want to insert values "the difference of it’s adj for sb and it's adj of sb" into PostgreSQL.

How I figure out this problem:

I replace ' with , and I replace " with '. Because PostgreSQL value does not support double quote.

So I think you can use following codes to insert values:

 insert into test values (1,'user’s log');
 insert into test values (2,'my users');
 insert into test values (3,'customer’s');
Wonz
  • 267
  • 5
  • 7
  • 1
    "*It means PostgreSQL can use only single quote for field names, table name*" no, you can **not** use single quotes `'` for column or table names. Neither in Postgres nor in standard SQL. And you can easily embed a single quote in a string constant `user''s log'` which is how this is defined in the SQL standard. –  Oct 29 '21 at 15:44
  • @a_horse_with_no_name Thanks for your solution. I find it works after testing. – Wonz Oct 29 '21 at 16:00
-1

You must have to add an extra single quotes -> ' and make doubling quote them up like below examples -> ' ' is the standard way and works of course:

Wrong way: 'user's log'
Right way: 'user''s log'

problem:

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

Solutions:

insert into test values (1,'user''s log');
insert into test values (2,'''my users''');
insert into test values (3,'customer''s');
MD. SHIFULLAH
  • 913
  • 10
  • 16