Unexpected substitution of SQL placeholder in this ruby database query - ruby

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?

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

Related

Prepared Statements Already Exists

I am trying to use the prepared statements in ruby with pg gem. This is how my statement looks like
conn.prepare("insert_values", "insert into " + objectName + "(" + headerStr + ") values (" + prep_values + ")")
conn.exec_prepared("insert_values", arr)
I keep getting the error
Prepared Statement insert_values already exists.
How Do i Fix this?? Thanks
Try to run:
conn.exec("DEALLOCATE name_of_prepared_statement")
In your example:
conn.exec("DEALLOCATE insert_values")
Simple test and it is working in my irb:
1.8.7 :001 > require 'rubygems'
=> true
1.8.7 :002 > require 'pg'
=> true
1.8.7 :003 > conn = PGconn.connect(:host => 'localhost', :port => 5912, :user => 'test', :dbname => 'test' )
=> #<PGconn:0x7fe6ac703970>
1.8.7 :005 > conn.prepare("insert_values", "select * from data where id < $1")
=> #<PGresult:0x7fe6ac6b2e58>
1.8.7 :006 > conn.prepare("insert_values", "select * from data where id < $1 and id > $2")
PGError: ERROR: prepared statement "insert_values" already exists
from (irb):6:in 'prepare'
from (irb):6
1.8.7 :007 > conn.prepare("insert_values", "select * from data where id < $1")
PGError: ERROR: prepared statement "insert_values" already exists
from (irb):7:in 'prepare'
from (irb):7
1.8.7 :008 > conn.exec("DEALLOCATE insert_values")
=> #<PGresult:0x7fe6ac6738c0>
1.8.7 :009 > conn.prepare("insert_values", "select * from data where id < $1")
=> #<PGresult:0x7fe6ac665fe0>
1.8.7 :010 > conn.exec_prepared("insert_values",[200])
=> #<PGresult:0x7fe6ac65d188>
1.8.7 :011 > conn.exec("DEALLOCATE insert_values")
=> #<PGresult:0x7fe6ac654df8>
1.8.7 :012 > conn.exec_prepared("insert_values",[200])
PGError: ERROR: prepared statement "insert_values" does not exist
from (irb):12:in 'exec_prepared'
from (irb):12
This is a better way reusing prepare(), avoid prepared statement "my_statement" already exists:
sql = "select * from my_table where id = $1"
begin
ActiveRecord::Base.connection.raw_connection.prepare('my_statement', sql)
rescue PG::DuplicatePstatement => e
end
pg_result = ActiveRecord::Base.connection.raw_connection.exec_prepared('my_statement', [my_table_id])

How can I escape a Ruby symbol with quotes only if needed?

IRB and Rails console both have a nice way of outputting symbols that only quote-escapes them when necessary. Some examples:
1.9.3p194 :001 > "#test".to_sym
=> :#test
1.9.3p194 :002 > "#Test".to_sym
=> :#Test
1.9.3p194 :003 > "#123".to_sym
=> :"#123"
1.9.3p194 :004 > "##_test".to_sym
=> :##_test
1.9.3p194 :005 > "test?".to_sym
=> :test?
1.9.3p194 :006 > "test!".to_sym
=> :test!
1.9.3p194 :007 > "_test!".to_sym
=> :_test!
1.9.3p194 :008 > "_test?".to_sym
=> :_test?
1.9.3p194 :009 > "A!".to_sym
=> :"A!"
1.9.3p194 :010 > "#a!".to_sym
=> :"#a!"
How would you do this yourself, so that you could do:
puts "This is valid code: #{escape_symbol(some_symbol)}"
The easiest and best way to do this is via Symbol's inspect method:
1.9.3p194 :013 > puts "This is valid code: #{"#a!".to_sym.inspect}"
This is valid code: :"#a!"
=> nil
1.9.3p194 :014 > puts "This is valid code: #{"a!".to_sym.inspect}"
This is valid code: :a!
You could look at the sym_inspect(VALUE sym) method in string.c in Ruby 1.9.3 that does that, if you're curious.
So, even though you don't need another method to call inspect, this would be the simplest implementation:
def escape_symbol(sym)
sym.inspect
end
Here's my attempt at implementing with a few regexs, although I'd suggest using inspect instead if you can:
def escape_symbol(sym)
sym =~ /^[#a-zA-Z_]#?[a-zA-Z_0-9]*$/ || sym =~ /^[a-z_][a-zA-Z_0-9]*[?!]?$/ ? ":#{sym}" : ":\"#{sym.gsub(/"/, '\\"')}\""
end

How do I access a value from a JSON string?

I have a variable. When I do puts var_name I get this hash:
"{\"numConnections\": 163}"
But when I try to get that number 163 from the value numConnections it isn't working. Here is what I am trying:
connections = temp_var["\"numConnections\""]
puts connections.inspect
or:
connections = temp_var["numConnections"]
puts connections.inspect
both of which equally don't work.
Any idea how to extract that 163 from there?
If you have a JSON string, you need to parse it into a hash before you can use it to access its keys and values in a hash-like way. Consider this IRB session:
1.9.3p194 :001 > require 'json'
=> true
1.9.3p194 :002 > temp_var = "{\"numConnections\": 163}"
=> "{"numConnections": 163}"
1.9.3p194 :003 > temp_var.class
=> String
1.9.3p194 :004 > JSON.parse(temp_var)
=> {"numConnections"=>163}
1.9.3p194 :005 > JSON.parse(temp_var)['numConnections']
=> 163

How do I extract the value of the glid parameter from this URL in ruby?

http://share.findmespot.com/shared/faces/viewspots.jsp?glId=0rEE45o3ERRryMevW5teqS9gkNI
You can do using the string split method. Easiest way.
ruby-1.9.2-p290 :004 > a = 'http://share.findmespot.com/shared/faces/viewspots.jsp?glId=0rEE45o3ERRryMevW5teqS9gkNI'
=> "http://share.findmespot.com/shared/faces/viewspots.jsp?glId=0rEE45o3ERRryMevW5teqS9gkNI"
ruby-1.9.2-p290 :005 > b = a.split('=')[1]
=> "0rEE45o3ERRryMevW5teqS9gkNI"

Are strings mutable in Ruby?

Are Strings mutable in Ruby? According to the documentation doing
str = "hello"
str = str + " world"
creates a new string object with the value "hello world" but when we do
str = "hello"
str << " world"
It does not mention that it creates a new object, so does it mutate the str object, which will now have the value "hello world"?
Yes, << mutates the same object, and + creates a new one. Demonstration:
irb(main):011:0> str = "hello"
=> "hello"
irb(main):012:0> str.object_id
=> 22269036
irb(main):013:0> str << " world"
=> "hello world"
irb(main):014:0> str.object_id
=> 22269036
irb(main):015:0> str = str + " world"
=> "hello world world"
irb(main):016:0> str.object_id
=> 21462360
irb(main):017:0>
Just to complement, one implication of this mutability is seem below:
ruby-1.9.2-p0 :001 > str = "foo"
=> "foo"
ruby-1.9.2-p0 :002 > ref = str
=> "foo"
ruby-1.9.2-p0 :003 > str = str + "bar"
=> "foobar"
ruby-1.9.2-p0 :004 > str
=> "foobar"
ruby-1.9.2-p0 :005 > ref
=> "foo"
and
ruby-1.9.2-p0 :001 > str = "foo"
=> "foo"
ruby-1.9.2-p0 :002 > ref = str
=> "foo"
ruby-1.9.2-p0 :003 > str << "bar"
=> "foobar"
ruby-1.9.2-p0 :004 > str
=> "foobar"
ruby-1.9.2-p0 :005 > ref
=> "foobar"
So, you should choose wisely the methods you use with strings in order to avoid unexpected behavior.
Also, if you want something immutable and unique throughout your application you should go with symbols:
ruby-1.9.2-p0 :001 > "foo" == "foo"
=> true
ruby-1.9.2-p0 :002 > "foo".object_id == "foo".object_id
=> false
ruby-1.9.2-p0 :003 > :foo == :foo
=> true
ruby-1.9.2-p0 :004 > :foo.object_id == :foo.object_id
=> true
While above answers are perfect, Just adding this answer for future readers.
In most languages, string literals are also immutable, just like
numbers and symbols. In Ruby versions that are less than 3, all
strings are mutable by default. This changed in Ruby 3. Now all string
literals are immutable by default in Ruby 3++ versions.

Resources