#ruby #sqlite #sqlite3-ruby
#ruby #sqlite #sqlite3-ruby
Вопрос:
Может кто-нибудь объяснить, что здесь происходит? Похоже, что синтаксис заполнения в строке инструкции SQL работает не так, как ожидалось (или, говоря по-другому, это нарушает принцип наименьшего удивления), и во время выполнения выполняется неожиданная замена / экранирование для var2
:
ruby-1.9.2-p290 :001 > puts RUBY_VERSION
1.9.2
=> nil
ruby-1.9.2-p290 :002 > require 'ipaddr'
=> true
ruby-1.9.2-p290 :003 > require 'sqlite3'
=> true
ruby-1.9.2-p290 :004 > var1 = Addrinfo.ip("1.2.3.4")
=> #<Addrinfo: 1.2.3.4>
ruby-1.9.2-p290 :005 > var2 = var1.ip_address
=> "1.2.3.4"
ruby-1.9.2-p290 :006 > var3 = "1.2.3.4"
=> "1.2.3.4"
ruby-1.9.2-p290 :007 > var2 == var3
=> true
ruby-1.9.2-p290 :008 > var2 === var3
=> true
ruby-1.9.2-p290 :009 > var2.eql?(var3)
=> true
ruby-1.9.2-p290 :010 > db = SQLite3::Database.open( "test.db" )
=> #<SQLite3::Database:0x00000100bcfce0>
ruby-1.9.2-p290 :011 > db.execute( "SELECT * FROM devices WHERE deviceaddr=?", var2 )
=> []
ruby-1.9.2-p290 :011 > db.execute( "SELECT * FROM devices WHERE deviceaddr=?", var2.to_s )
=> []
ruby-1.9.2-p290 :012 > db.execute( "SELECT * FROM devices WHERE deviceaddr=?", var3 )
=> [["TEST_DEVICE", "1.2.3.4"]]
Без заполнителя SQL это работает (но подвергает БД SQL-инъекциям!):
ruby-1.9.2-p290 :013 > db.execute( "SELECT * FROM devices WHERE deviceaddr='#{var2}'" )
=> [["TEST_DEVICE", "1.2.3.4"]]
Итак, каков безопасный способ заставить это работать?
Ответ №1:
TL; DR: SQLite использует UTF; преобразуйте 8-разрядный вывод ASCII Addrinfo.
Один «безопасный» способ — использовать force_encoding("UTF-8")
на выходе из Addrinfo
, так:
> var1.ip_address.encoding
=> #<Encoding:ASCII-8BIT>
> var3.encoding
=> #<Encoding:UTF-8>
> db.execute("SELECT * FROM foo WHERE ip=?", var2.force_encoding("UTF-8"))
=> [["1.2.3.4"]]
Комментарии:
1. Мне нравится, что для ответа длиной в 2 строки, исключая пример кода, требуется TL; DR 🙂
2. @mikej Для аллергиков на код или тех, кто знает лучший способ 🙂 (И я кое-что отредактировал.)
3. Да, это правильно. Я должен добавить только одно: вероятно, лучший способ определить правильную кодировку — из самой базы данных: ‘enc= db.encoding’, а затем выполнить var1.ip_address.encode ( enc ) или var2.encode( enc)