Claudio Floreani
Claudio Floreani

Reputation: 2571

Unexpected substitution of SQL placeholder in this ruby database query

Can someone explain what's happening here? It seems that the placeholding syntax in SQL statement string doesn't work as expected (or, to say it in a different way, it violates the principle of least surprise), and during runtime an unexpected substitution/escaping is done for 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"]] 

Without the SQL placeholder it works (but exposes the db to SQL injections!):

ruby-1.9.2-p290 :013 > db.execute( "SELECT * FROM devices WHERE deviceaddr='#{var2}'" )
=> [["TEST_DEVICE", "1.2.3.4"]] 

So what is a safe way to make this work?

Upvotes: 2

Views: 486

Answers (1)

Dave Newton
Dave Newton

Reputation: 160211

TL;DR: SQLite uses UTF; convert Addrinfo's 8-bit ASCII output.

One "safe" way is to use force_encoding("UTF-8") on the output from Addrinfo, so:

> 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"]] 

Upvotes: 4

Related Questions