Masukkan teks dengan tanda kutip tunggal dalam PostgreSQL

433

Saya punya meja test(id,name).

Saya harus memasukkan nilai-nilai seperti: 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');

Saya mendapatkan kesalahan jika saya menjalankan salah satu dari pernyataan di atas.

Jika ada metode untuk melakukan ini dengan benar, silakan bagikan. Saya tidak ingin ada pernyataan yang disiapkan.

Apakah mungkin menggunakan mekanisme melarikan diri sql?

MAHI
sumber
1
Gunakan nilai apa pun yang diberikan perpustakaan klien Anda. Untuk informasi lebih lanjut, Anda harus mengatakan bagaimana Anda mengakses database.
Richard Huxton
Basis data @Richard Huxton diakses oleh java.
MAHI
2
Jadi gunakan placeholder jdbc standar. Atau jelaskan mengapa itu bukan pilihan terbaik.
Richard Huxton
@ Richard Huxton saya tidak mengatakan itu bukan pilihan terbaik, saya mencari apakah mereka ada metode melarikan diri dalam sql untuk melakukannya.
MAHI
Baiklah, lihat jawaban @ Claudix di bawah ini, tetapi yang jelas menghargai literal akan membutuhkan pelarian yang berbeda tergantung pada tipe postgresql.org/docs/current/static/datatype.html
Richard Huxton

Jawaban:

764

Literal string

Meloloskan satu kutipan 'dengan menggandakannya -> ''adalah cara dan cara standar tentu saja:

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

Dalam versi lama atau jika Anda masih menjalankan dengan standard_conforming_strings = offatau, secara umum, jika Anda menambahkan string Anda dengan Emendeklarasikan sintaks string Posix , Anda juga dapat melarikan diri dengan backslash \:

E'user\'s log'

Backslash sendiri lolos dengan backslash lain. Tapi itu umumnya tidak disukai.
Jika Anda harus berurusan dengan banyak tanda kutip tunggal atau beberapa lapisan pelolosan, Anda dapat menghindari mengutip neraka di PostgreSQL dengan string yang dikutip dolar :

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

Untuk menghindari kebingungan di antara kutipan dolar, lebih lanjut tambahkan token unik untuk setiap pasangan:

$token$escape ' with ''$token$

Yang dapat disarangkan sejumlah level:

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

Perhatikan jika $karakter tersebut memiliki makna khusus dalam perangkat lunak klien Anda. Anda mungkin harus menghindarinya juga. Ini tidak terjadi dengan klien PostgreSQL standar seperti psql atau pgAdmin.

Itu semua sangat berguna untuk menulis fungsi plpgsql atau perintah SQL ad-hoc. Itu tidak dapat mengurangi kebutuhan untuk menggunakan pernyataan yang disiapkan atau beberapa metode lain untuk melindungi terhadap injeksi SQL dalam aplikasi Anda ketika input pengguna dimungkinkan. @ Craig menjawab lebih banyak tentang itu. Keterangan lebih lanjut:

Nilai di dalam Postgres

Saat berurusan dengan nilai-nilai di dalam database, ada beberapa fungsi yang berguna untuk mengutip string dengan benar:

  • quote_literal()atauquote_nullable() - yang terakhir menampilkan string NULLuntuk input nol. (Ada juga quote_ident()untuk double-kutipan string di mana diperlukan untuk mendapatkan SQL yang valid pengidentifikasi .)
  • format()dengan format specifier %Lsetara dengan quote_nullable().
    Suka:format('%L', string_var)
  • concat()atauconcat_ws() biasanya tidak baik karena mereka tidak luput dari tanda kutip tunggal dan backslash.
Erwin Brandstetter
sumber
1
Perlu juga dicatat bahwa beberapa versi PgJDBC memiliki masalah dengan pengutipan dolar - khususnya, mungkin gagal mengabaikan terminator pernyataan (;) dalam string yang dikutip dalam dolar.
Craig Ringer
1
Jawaban terkait ini memiliki detail untuk masalah dengan JDBC.
Erwin Brandstetter
1
Dan jika Anda ingin melarikan diri s'tring dari kolom teks pada penyisipan dalam kasus bahasa prosedural dll, maka Anda dapat menggunakan fungsi string quote_literal (column_name).
alexglue
1
$ token $ is awesome. Terima kasih.
mythicalcoder
@ ErwinBrandstetter, kembali "dapat disarangkan sejumlah tingkat": tetapi SELECT $outer$OUT$inner$INNER$inner$ER$outer$;membuktikan bahwa tingkat 2 bersarang tidak berfungsi di sini.?
filiprem
46

Ini sangat banyak dunia yang buruk, karena pertanyaan Anda menyiratkan bahwa Anda mungkin memiliki lubang injeksi SQL yang menganga dalam aplikasi Anda.

Anda harus menggunakan pernyataan parameter. Untuk Java, gunakan PreparedStatementdengan placeholder . Anda mengatakan Anda tidak ingin menggunakan pernyataan parameter, tetapi Anda tidak menjelaskan mengapa , dan terus terang itu harus menjadi alasan yang sangat baik untuk tidak menggunakannya karena mereka adalah cara termudah, teraman untuk memperbaiki masalah yang Anda coba menyelesaikan.

Lihat Mencegah Injeksi SQL di Jawa . Jangan menjadi korban Bobby berikutnya.

Tidak ada fungsi publik di PgJDBC untuk mengutip dan melarikan diri string. Itu sebagian karena itu mungkin membuatnya tampak seperti ide yang bagus.

Ada yang built-in mengutip fungsi quote_literaldan quote_identdi PostgreSQL, tetapi mereka adalah untuk PL/PgSQLfungsi yang digunakan EXECUTE. Hari-hari quote_literalini sebagian besar sudah usang oleh EXECUTE ... USING, yang merupakan versi parameter , karena lebih aman dan lebih mudah . Anda tidak dapat menggunakannya untuk tujuan yang Anda jelaskan di sini, karena mereka adalah fungsi sisi server.


Bayangkan apa yang terjadi jika Anda mendapatkan nilai ');DROP SCHEMA public;--dari pengguna jahat. Anda akan menghasilkan:

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

yang terbagi menjadi dua pernyataan dan komentar yang diabaikan:

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

Aduh, ini database Anda.

Craig Ringer
sumber
Saya cenderung setuju dengan satu pengecualian - "di mana" klausa (meskipun ia mengatakan "menyisipkan") dengan daftar nilai sebagai bagian dari klausa "dalam" (atau sekelompok "atau" s). Saya kira Anda bisa menghitung ukuran daftar dan menghasilkan teks ke pernyataan yang disiapkan dengan klausa "dalam", tetapi itu menjadi aneh dalam kasus penggunaan itu.
Roboprog
@Roboprog Dengan beberapa driver klien yang dapat Anda gunakan = ANY(?)dan parameter array.
Craig Ringer
12
Saya sering menggunakan sisipan literal seperti ini untuk mem-bootstrap data, bersama dengan DDL. Mari kita coba menjawab pertanyaan daripada jawaban seperti 'Anda salah'
ThatDataGuy
1
@ThatDataGuy memberikan komentar yang adil, tetapi dalam pertanyaan ini OP menambahkan komentar yang mengatakan database is accessed by javahal ini secara langsung menjawab pertanyaan tersebut. Hal ini juga sangat penting bagi orang-orang yang datang ke sini untuk menyadari bahaya potensial, terutama mengingat SQL Injection adalah penyebab # 1 kerentanan perangkat lunak. Setelah menyadari masalah ini, orang-orang dapat membuat keputusan yang tepat kapan hal itu tidak penting, seperti kasing bootstrap Anda.
Davos
Persis. Orang-orang juga banyak menyalin & menempel kode. Saya akan berhenti memperingatkan orang tentang ini pada hari saya berhenti melihat kerentanan injeksi SQL setiap hari dalam kode produksi.
Craig Ringer
26

Menurut dokumentasi 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'.

Lihat juga parameter standard_conforming_strings , yang mengontrol apakah melarikan diri dengan backslash berfungsi.

Claudix
sumber
terima kasih atas balasan, tetapi saya harus secara manual melarikan diri masing-masing char dengan menggunakan ini, jika ada fungsi built-in untuk melakukan ini?
MAHI
3
@ MAHI Jika ada fungsi seperti itu, itu akan di PgJDBC, bukan di PostgreSQL sendiri, karena pelarian harus dilakukan di sisi klien. Tidak ada fungsi publik yang didokumentasikan seperti itu karena itu adalah ide yang mengerikan . Anda harus menggunakan pernyataan berparameter sehingga Anda tidak perlu melakukan pelarian yang berpotensi tidak dapat diandalkan.
Craig Ringer
13

Dalam postgresql jika Anda ingin memasukkan nilai 'di dalamnya maka untuk ini Anda harus memberikan ekstra'

 insert into test values (1,'user''s log');
 insert into test values (2,'''my users''');
 insert into test values (3,'customer''s');
Pemburu
sumber
pilih untuk menunjukkan tiga kutipan jika Anda memiliki string yang dikutip
winkbrace
Facebook, karena ini adalah solusi sederhana
ktaria
5

Anda dapat menggunakan fungsi postrgesql chr (int):

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

Jika Anda perlu menyelesaikan pekerjaan dalam Pg:

to_json(value)

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

hatenine
sumber
Bagaimana pertanyaan ini terkait dengan JSON?
Erwin Brandstetter
1
@ErwinBrandstetter, maaf, saya mungkin pergi .. tapi lolos dari
petikan
1
Itu masalah lain sama sekali. Anda mungkin menggunakan format(), quote_literal()atau quote_nullable()untuk menghindari kutip. Lihat: stackoverflow.com/a/25143945/939860
Erwin Brandstetter
0
select concat('''','abc','''')
Rushi The Sharma
sumber
Ini perlu mengutip user's logdi tempat yang abctepat untuk memulai. Tangkap 22.
Erwin Brandstetter