PostgreSQL'e tek tırnak işareti içeren metin ekleme


432

Bir masam var test(id,name).

Ben gibi değerleri eklemek gerekir: 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');

Yukarıdaki ifadelerden herhangi birini çalıştırırsanız bir hata alıyorum.

Bunu doğru yapmak için herhangi bir yöntem varsa lütfen paylaşın. Hazırlanmış bir ifade istemiyorum.

Sql kaçış mekanizması kullanmak mümkün mü?


1
İstemci kitaplığınızdan kaçan değeri kullanın. Daha fazla bilgi için veritabanına nasıl eriştiğinizi söylemeniz gerekir.
Richard Huxton

@Richard Huxton veritabanına java tarafından erişilir.
MAHI

2
Bu yüzden standart jdbc yer tutucularını kullanın. Ya da bunun neden en iyi seçenek olmadığını açıklayın.
Richard Huxton

@Richard Huxton ben bunun en iyi seçim olmadığını söylemiyorum, ben bunu yapmak için sql herhangi bir kaçan yöntem varsa arama yapıyorum.
MAHI

Aşağıdaki @ Claudix'in cevabına bakın, ancak açık bir şekilde değer değişmezleri, postgresql.org/docs/current/static/datatype.html
Richard Huxton

Yanıtlar:


763

Dize değişmez değerleri

Tek tırnakları 'ikiye katlayarak kaçmak -> ''standart yoldur ve elbette çalışır:

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

Eski sürümlerde veya hala standard_conforming_strings = offveya ile çalışıyorsanız, genellikle, Posix kaçış dizesi sözdiziminiE bildirmek için dizenizi başa eklerseniz, ters eğik çizgiyle de kaçabilirsiniz :\

E'user\'s log'

Ters eğik çizginin kendisi başka bir ters eğik çizgiyle kaçtı. Ancak bu genellikle tercih edilmez.
Birçok tek tırnak veya birden fazla kaçış katmanı ile uğraşmak zorunda kalırsanız, PostgreSQL'de dolar cinsinden dizelerle cehennemden kaçınabilirsiniz :

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

Dolar-kotasyonlar arasında karışıklığı önlemek için, her bir çift için benzersiz bir jeton ekleyin:

$token$escape ' with ''$token$

Hangi sayıda seviye iç içe olabilir:

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

$Karakterin istemci yazılımınızda özel bir anlamı olması gerektiğine dikkat edin . Ayrıca kaçmak zorunda kalabilirsiniz. Psql veya pgAdmin gibi standart PostgreSQL istemcilerinde durum böyle değildir.

Tüm bunlar plpgsql fonksiyonlarını veya geçici SQL komutlarını yazmak için çok kullanışlıdır. Bununla birlikte, kullanıcı girişi mümkün olduğunda uygulamanızda SQL enjeksiyonuna karşı koruma sağlamak için hazırlanmış ifadeler veya başka bir yöntem kullanma ihtiyacını azaltamaz. @ Craig'in cevabı bundan daha fazlasını içeriyor. Daha fazla detay:

Postgres içindeki değerler

Veritabanı içindeki değerlerle uğraşırken, dizeleri doğru şekilde alıntılamak için birkaç yararlı işlev vardır:

  • quote_literal()veyaquote_nullable() - ikincisi NULLboş girdi için dizeyi çıktılar . ( Geçerli SQL tanımlayıcılarını almak için gerektiğinde dizeleri çift ​​tırnak olarak da quote_ident()kullanabilirsiniz .)
  • format()biçimlendirici %Lile eşdeğerdir quote_nullable().
    Sevmek:format('%L', string_var)
  • concat()veyaconcat_ws() iç içe tek tırnak ve ters eğik çizgilerden kaçmadığından genellikle iyi değildir .

1
Ayrıca, bazı PgJDBC sürümlerinin dolar alıntılama ile ilgili sorunları olduğunu da belirtmek gerekir - özellikle dolar cinsinden dizelerde ifade sonlandırıcılarını (;) göz ardı edemeyebilir.
Craig Ringer

1
Bu ilgili yanıt , JDBC'deki sorunla ilgili ayrıntılara sahiptir.
Erwin Brandstetter

1
Ve u yordamsal dil vb. Eklenmesi durumunda ekleme sırasında metin sütunundan s'tring kaçmak istiyorsanız, quote_literal (column_name) dize işlevini kullanabilirsiniz.
alexglue

1
$ jeton $ harika. Teşekkürler.
efsanevi

@ErwinBrandstetter, yeniden "herhangi bir sayıda SELECT $outer$OUT$inner$INNER$inner$ER$outer$;seviye yuvalanabilir ": ancak 2. seviye yuvalamanın burada çalışmadığını kanıtlıyor.?
filiprem

46

Bu çok kötü dünyalar, çünkü sorunuz muhtemelen uygulamanızda boşluk bırakan SQL enjeksiyon deliklerine sahip olduğunuzu ima ediyor .

Parametreli ifadeler kullanıyor olmalısınız. Java PreparedStatementiçin yer tutucularla kullanın . Parametreli ifadeler kullanmak istemediğinizi söylüyorsunuz, ancak nedenini açıklamıyorsunuz ve açıkçası bunları kullanmamanın çok iyi bir nedeni olmalı çünkü bunlar, denediğiniz sorunu çözmenin en basit ve en güvenli yoludur çözmek için.

Java'da SQL Enjeksiyonunu Önleme konusuna bakın . Olmayın Bobby bireyin sonraki kurban.

PgJDBC'de dize alıntılama ve kaçma için genel bir işlev yoktur. Bunun nedeni kısmen iyi bir fikir gibi görünmesine neden olabilir.

Orada olan yerleşik işlevleri alıntı quote_literalve quote_identPostgreSQL de, ancak içindir PL/PgSQLkullanım olduğu fonksiyonlar EXECUTE. Bugünlerde quote_literalçoğunlukla tarafından obsoleted edilir EXECUTE ... USINGki, Parametrelenmiş versiyonu o çünkü, daha güvenli ve daha kolay . Bunları burada açıkladığınız amaç için kullanamazsınız, çünkü bunlar sunucu tarafı işlevleridir.


Değeri ');DROP SCHEMA public;--kötü niyetli bir kullanıcıdan alırsanız ne olacağını hayal edin . Sen üreteceksin:

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

iki ifadeye ve yok sayılan bir yoruma ayrılır:

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

Hata! Veritabanınız gidiyor.


Ben bir istisna - "burada" yan tümceleri ("insert" diyor olsa da) bir "in" yan tümcesi (veya bir demet "veya" s) bir parçası olarak bir değer listesi ile kabul eğilimi. Sanırım listenin boyutunu sayabilir ve "in" yan tümcesiyle hazırlanmış ifadeye metin oluşturabilirsiniz, ancak bu kullanım durumunda garipleşir.
Roboprog

@Roboprog Bazı istemci sürücüleri = ANY(?)ve bir dizi parametresi kullanabilirsiniz.
Craig Ringer

12
DDL ile birlikte verileri önyüklemek için genellikle böyle bir ekler kullandım. Sadece 'yanlış yapıyorsun' gibi cevaplara göre soruları cevaplamaya çalışalım
ThatDataGuy

1
@ThatDataGuy adil yorum, ancak bu soruda OP bir yorum ekledi, database is accessed by javaböylece bu doğrudan soruyu ele alıyor. Buraya gelen insanların potansiyel tehlikelerden haberdar edilmesi de çok önemlidir, özellikle SQL Injection yazılım güvenlik açığının 1 numaralı nedenidir. Sorunun farkında olduklarında, insanlar önyükleme kullanım durumunuz gibi, ne zaman önemli olmadığı konusunda bilinçli kararlar verebilirler.
Davos

Kesinlikle. İnsanlar da kodu çok kopyalayıp yapıştırıyor. Üretim kodunda günlük olarak SQL enjeksiyon güvenlik açıklarını görmeyi bıraktığım gün insanları bu konuda uyarmayı bırakacağım.
Craig Ringer

26

PostgreSQL belgelerine göre (4.1.2.1. Dize Sabitleri) :

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

Ayrıca ters eğik çizgilerden kaçmanın işe yarayıp yaramadığını kontrol eden standard_conforming_strings parametresine de bakın .


cevap için teşekkürler, ama ben bunu yapmak için herhangi bir yerleşik fonksiyonları varsa, bu kullanarak her char kaçmak zorunda?
MAHI

3
@MAHI Böyle bir işlev olsaydı, PostgreSQL'de değil PgJDBC'de olurdu, çünkü kaçma işlemi istemci tarafında yapılmalıdır. Böyle belgelenmiş bir kamu işlevi yoktur, çünkü bu korkunç bir fikirdir. Parametrelenmemiş ifadeler kullanmalısınız, böylece güvenilir olmayan herhangi bir çıkış yapmanıza gerek kalmaz.
Craig Ringer

13

Postgresql'de içine değerler eklemek istiyorsanız ', bunun için ekstra vermeniz gerekir.'

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

alıntılanmış bir dizeniz varsa üçlü alıntıları göstermek için
upvote

, basit bir çözüm olduğu için
ktaria

5

postrgesql chr (int) işlevini kullanabilirsiniz:

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

2

İşin Pg içinde yapılması gerekiyorsa:

to_json(value)

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


Bu soru JSON ile nasıl ilişkilidir?
Erwin Brandstetter

1
@ErwinBrandstetter, üzgünüm, kapalı olabilirim ... ama dizelerde tırnaklardan kurtulur
hatenine

1
Bu tamamen başka bir mesele. Sen kullanabilir format(), quote_literal()ya da quote_nullable()tırnak kaçmak için. Bkz. Stackoverflow.com/a/25143945/939860
Erwin Brandstetter

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.