Rails 4 LIKE query - ActiveRecord menambahkan tanda kutip

127

Saya mencoba melakukan kueri yang mirip seperti itu

def self.search(search, page = 1 )
  paginate :per_page => 5, :page => page,
    :conditions => ["name LIKE '%?%' OR postal_code like '%?%'", search, search],   order => 'name'
end

Tetapi ketika dijalankan sesuatu menambahkan tanda kutip yang menyebabkan pernyataan sql keluar seperti itu

SELECT COUNT(*)
FROM "schools" 
WHERE (name LIKE '%'havard'%' OR postal_code like '%'havard'%')):

Jadi Anda bisa melihat masalah saya. Saya menggunakan Rails 4 dan Postgres 9 yang keduanya belum pernah saya gunakan jadi tidak yakin apakah itu dan hal activerecord atau mungkin hal postgres.

Bagaimana saya bisa mengatur ini jadi saya punya seperti '%my_search%'pada permintaan akhir?

Harry Forbess
sumber

Jawaban:

228

Penampung Anda diganti oleh string dan Anda tidak menanganinya dengan benar.

Menggantikan

"name LIKE '%?%' OR postal_code LIKE '%?%'", search, search

dengan

"name LIKE ? OR postal_code LIKE ?", "%#{search}%", "%#{search}%"
rb512
sumber
6
Bukankah ini rentan terhadap SQL Injection? Maksudku, apakah searchsenar itu sudah bersih?
jdscosta91
6
@ jdscosta91 ?di mana akan mengurus sanitasi
house9
7
@ House9 contoh dari %dan _di dalam searchtidak akan disanitasi, di bawah pendekatan ini.
Barry Kelly
8
Ketika menggunakan '?' dengan cara ini di rails dikonversi ke permintaan parameter. Data dalam parameter tidak disanitasi (%) tetapi tidak mungkin mengubah konteks dari kueri dan mengubahnya menjadi pernyataan SQL yang diproses.
David Hoelzer
50

Alih-alih menggunakan conditionssintaks dari Rails 2, gunakan wheremetode Rails 4 sebagai gantinya:

def self.search(search, page = 1 )
  wildcard_search = "%#{search}%"

  where("name ILIKE :search OR postal_code LIKE :search", search: wildcard_search)
    .page(page)
    .per_page(5)
end

CATATAN: di atas menggunakan sintaks parameter bukan? placeholder: keduanya harus menghasilkan sql yang sama.

def self.search(search, page = 1 )
  wildcard_search = "%#{search}%"

  where("name ILIKE ? OR postal_code LIKE ?", wildcard_search, wildcard_search)
    .page(page)
    .per_page(5)
end

CATATAN: menggunakan ILIKEnama LIKE - versi postgres case sensitif dari LIKE

rumah9
sumber
Apakah ini masih berlaku untuk Rails 5? Karena jika saya memilikinya Movie.where("title ILIKE :s", s: search_string)akan diterjemahkan SELECT 1 AS one FROM "movies" WHERE (title ILIKE 'test') LIMIT $1oleh ActiveRecord (Rails 5.1.6) - harap perhatikan bahwa tidak ada simbol persentase setelah ILIKE)
sekmo
@sekmo - itu harus bekerja, persentase akan masuk dalam search_stringvariabel. Saya pikir SELECT 1 AS oneoutput hanya di konsol rel atau Anda gunakan limit(1)? FYI: Rails 5.1.6 memiliki masalah keamanan, gunakan 5.1.6.2 atau 5.1.7 sebagai gantinya
house9
22

Meskipun interpolasi string akan berfungsi, karena pertanyaan Anda menentukan rel 4, Anda bisa menggunakan Arel untuk ini dan menjaga agnostik basis data aplikasi Anda.

def self.search(query, page=1)
  query = "%#{query}%"
  name_match = arel_table[:name].matches(query)
  postal_match = arel_table[:postal_code].matches(query)
  where(name_match.or(postal_match)).page(page).per_page(5)
end
numbers1311407
sumber
Seharusnya benar-benar mudah untuk membuat fungsi untuk melakukan ini secara dinamis dengan daftar atribut
cevaris
Tidak teruji, meskipun bisa berupa ini: scope search_attributes, -> (permintaan, atribut) {arel_attributes = atribut.map {| a | arel_table [a]} arel_queries = arel_attributes.map {| a | a.matches (permintaan)} kembali ke mana (arel_queries.reduce {| res, q | res.or q})}
Pedro Rolo
8

ActiveRecord cukup pintar untuk mengetahui bahwa parameter yang dirujuk oleh ?adalah string, dan dengan demikian membungkusnya dalam tanda kutip tunggal. Anda bisa sebagai satu posting menyarankan menggunakan interpolasi string Ruby untuk mengisi string dengan %simbol yang diperlukan . Namun, ini bisa membuat Anda terkena SQL-injection (yang buruk). Saya sarankan Anda menggunakan CONCAT()fungsi SQL untuk mempersiapkan string seperti:

"name LIKE CONCAT('%',?,'%') OR postal_code LIKE CONCAT('%',?,'%')", search, search)

John Cleary
sumber
Saya baru saja mengimplementasikan ini dalam suatu aplikasi dan itu bekerja dengan baik. John terima kasih
fuzzygroup
mengenai injeksi, tidak, dan dalam hal apa pun tidak ada bedanya. String dimasukkan ke dalam sql dan rails seharusnya sudah memvalidasinya sebelumnya (tidak masalah jika a %ditambahkan / ditambahkan atau tidak). Entah itu berfungsi seperti yang diharapkan atau rel memiliki bug utama yang mempengaruhi kedua kasus.
estani
5

Mencoba

 def self.search(search, page = 1 )
    paginate :per_page => 5, :page => page,
      :conditions => ["name LIKE  ? OR postal_code like ?", "%#{search}%","%#{search}%"],   order => 'name'
  end

Lihat dokumen tentang kondisi AREL untuk info lebih lanjut.

tihom
sumber
-1
.find(:all, where: "value LIKE product_%", params: { limit: 20, page: 1 })
Jeff
sumber