Escaping apostrophe in Ruby - ruby-on-rails

I am trying to escape a single quote in a ruby string. I am using this string as a insert query to push data into the postgresql.
The query that will be generated looks something like this:-
str = insert into table field1,field2 values 'Gaurav's', 'Scooter'
I tried escaping it with
str.gsub("/'/",\\\\')
But this didn't work.
The error that I always get is:
Syntax Error Near s:
I guess i would need a regex to escape single quote inside the two single quotes not sure though.
How do I fix this? Thank you.

You should used prepared statements. Prepared statements help increase your speed. The query is parsed once by the DB. They also help you avoid having to do manual escaping as you are trying to do.
f1_val = "Gaurav's"
f2_val = "Scooter"
# conn is your connection object
conn.prepare('givethisqueryaname', "INSERT INTO table field1,field2 VALUES ($1,$2)")
conn.exec_prepared('givethisqueryaname',[f1_val, f2_val])
If you are given the field names, and field values as a string, then you can do this.
fieldStr = "field1,field2"
valuesStr = "Gaurav's, Scooter"
arr = valuesStr.split(",")
conn.prepare('insert_x', "INSERT INTO table #{fieldStr} VALUES ($1,$2)")
conn.exec_prepared('insert_x', arr)

This works if the single quote you want to escape always is in front of a "s"
1.9.3p125 :020 > str = "insert into table field1,field2 values 'Gaurav's', 'Scooter'"
=> "insert into table field1,field2 values 'Gaurav's', 'Scooter'"
1.9.3p125 :021 > str.gsub("'s","-s")
=> "insert into table field1,field2 values 'Gaurav-s', 'Scooter'"
You can user the difference between single quotes(') and double quotes(").

Related

convert my string to comma based elements

I am working on a legacy Rails project that relies on Ruby version 1.8
I have a string looks like this:
my_str = "a,b,c"
I would like to convert it to
value_list = "('a','b','c')"
so that I can directly use it in my SQL statement like:
"SELECT * from my_table WHERE value IN #{value_list}"
I tried:
my_str.split(",")
but it returns "abc" :(
How to convert it to what I need?
To split the string you can just do
my_str.split(",")
=> ["a", "b", "c"]
The easiest way to use that in a query, is using where as follows:
Post.where(value: my_str.split(","))
This will just work as expected. But, I understand you want to be able to build the SQL-string yourself, so then you need to do something like
quoted_values_str = my_str.split(",").map{|x| "'#{x}'"}.join(",")
=> "'a','b','c'"
sql = ""SELECT * from my_table WHERE value IN (#{quoted_values_str})"
Note that this is a naive approach: normally you should also escape quotes if they should be contained inside your strings, and makes you vulnerable for sql injection. Using where will handle all those edge cases correctly for you.
Under no circumstances should you reinvent the wheel for this. Rails has built-in methods for constructing SQL strings, and you should use them. In this case, you want sanitize_sql_for_assignment (aliased to sanitize_sql):
my_str = "a,b,c"
conditions = sanitize_sql(["value IN (?)", my_str.split(",")])
# => value IN ('a','b','c')
query = "SELECT * from my_table WHERE #{conditions}"
This will give you the result you want while also protecting you from SQL injection attacks (and other errors related to badly formed SQL).
The correct usage may depend what version of Rails you're using, but this method exists as far back as Rails 2.0 so it will definitely work even with a legacy app; just consult the docs for the version of Rails you're using.
value_list = "('#{my_str.split(",").join("','")}')"
But this is a very bad way to query. You better use:
Model.where(value: my_str.split(","))
The string can be manipulated directly; there is no need to convert it to an array, modify the array then join the elements.
str = "a,b,c"
"(%s)" % str.gsub(/([^,]+)/, "'\\1'")
#=> "('a','b','c')"
The regular expression reads, "match one or more characters other than commas and save to capture group 1. \\1 retrieves the contents of capture group 1 in the formation of gsub's replacement string.
couple of use cases:
def full_name
[last_name, first_name].join(' ')
end
or
def address_line
[address[:country], address[:city], address[:street], address[:zip]].join(', ')
end

how to query for activerecord's select_value method?

can anyone please tell me how to write query in select_value.
I have tried,
ActiveRecord::Base.connection.select_value("select count(*) from leave_details where status= 'Pending' and 'employeedetails_id'=25")
but it showing error
invalid input syntax for integer: "employeedetails_id".
I am using PostgreSQL.
Single quotes are used to quote strings in PostgreSQL (and every other SQL database that even pretends to respect the SQL standard) so you're saying something like this:
some_string = some_integer
when you do this:
'employeedetails_id'=25
and that doesn't make any sense: you can't compare strings and integers without an explicit type cast. You don't need to quote that identifier at all:
ActiveRecord::Base.connection.select_value(%q{
select count(*)
from leave_details
where status = 'Pending'
and employeedetails_id = 25
})
If you even do need to quote an identifier (perhaps it is case sensitive or contains spaces), then you'd use double quotes with PostgreSQL.
Apparently you created your column as "EmployeeDetails_id" so that it is case sensitive. That means that you always have to use that case and you always have to double quote it:
ActiveRecord::Base.connection.select_value(%q{
select count(*)
from leave_details
where status = 'Pending'
and "EmployeeDetails_id" = 25
})
I'd recommend reworking your table to not use mixed case identifiers:
They go against standard Ruby/Rails naming.
They force you to double quote the mixed case column names everywhere you use them.
They go against standard PostgreSQL practice.
This is going to trip you up over and over again.
Executing SQL directly isn't really The Rails Way, and you lose any database portability by doing it that way.
You should create a model for leave_details. E.g.
rails g model LeaveDetails status:string employeedetails_id:integer
Then, the code would be:
LeaveDetails.where({ :status => 'Pending', :employeedetails_id => 25 }).count

sanitize_sql_array is adding extra, unnecessary quotes to query

This is the first time I've seen this issue. I'm building up an SQL array to run through sanitize_sql_array and Rails is adding extra, unnecessary single quotes in the return value. So instead of returning:
SELECT DISTINCT data -> 'Foo' from products
it returns:
SELECT DISTINCT data -> ''Foo'' from products
which of course Postgres doesn't like.
Here is the code:
sql_array = ["SELECT DISTINCT %s from products", "data -> 'Foo'"]
sql_array = sanitize_sql_array(sql_array)
connection.select_values(sql_array)
Note the same thing happens when I use the shorter and more usual:
sql_array = ["SELECT DISTINCT %s from products", "data -> 'Foo'"]
connection.select_values(send(:sanitize_sql_array, sql_array))
Ever seen this before? Does it have something to do with using HStore? I definitely need that string sanitized since the string Foo is actually coming from a user-entered variable.
Thanks!
You're giving sanitize_sql_array a string that contains an hstore expression and expecting sanitize_sql_array to understand that the string contains some hstore stuff; that's asking far too much, sanitize_sql_array only knows about simple things like strings and numbers, it doesn't know how to parse PostgreSQL's SQL extensions or even standard SQL. How would you expect sanitize_sql_array to tell the difference between, for example, a string that happens to contain '11 * 23' and a string that is supposed to represent the arithmetical expression 11 * 23?
You should split your data -> 'Foo' into two pieces so that sanitize_sql_array only sees the string part when it is sanitizing things:
sql_array = [ 'select distinct data -> ? from products', 'Foo' ]
sql = sanitize_sql_array(sql_array)
That will give you the SQL you're looking for:
select distinct data -> 'Foo' from products

Cannot query for a long string in ActiveRecord

I have an active record Song model with a songhash field (string(255)) that contains a Sha2 hash. When I try to find a song via the following code nothing gets returned:
song = Song.all.first
song2 = Song.where(songhash: song.songhash).first
# song is a valid object with a songhash set, but song2 is nil!
If I do the same thing however with a "like" query it works:
song = Song.all.first
song2 = Song.where("songhash like ?", song.songhash).first
# song2 is a valid object now
song2.songhash == song.songhash
# the equation is true
I fear it has something to do with string encodings but I have no idea why this string could possibly have encoding issues: 61a9761b9ebd543b72c5ccf2ab6db198b067f7cf7f8412ee6e9c14b19611bc80
I'm using rails 3.1 with sqlite db.
Any ideas what's going on?
Summary
SQL statements generated
# With = / It doesn't work
SELECT "songs".* FROM "songs" WHERE "songs"."type"
IN ('PlaylistSong') AND "songs"."songhash" =
'61a9761b9ebd543b72c5ccf2ab6db198b067f7cf7f8412ee6e9c14b19611bc80'
# With like / It works
SELECT "songs".* FROM "songs" WHERE "songs"."type"
IN ('PlaylistSong') AND (songhash like
'61a9761b9ebd543b72c5ccf2ab6db198b067f7cf7f8412ee6e9c14b19611bc80')
# With upper / It works
SELECT "songs".* FROM "songs" WHERE "songs"."type"
IN ('PlaylistSong') AND (UPPER(songhash) =
'61A9761B9EBD543B72C5CCF2AB6DB198B067F7CF7F8412EE6E9C14B19611BC80')
The following statements work:
Song.where(['UPPER(songhash) = ?', song.songhash.upcase]).first
Song.where(['songhash like ?', song.songhash]).first
UPPER and LIKE are both case insensitive
SQLite documentation
The LIKE operator does a pattern matching comparison. (A bug: SQLite only understands upper/lower case for ASCII characters by default. The LIKE operator is case sensitive by default for unicode characters that are beyond the ASCII range. For example, the expression 'a' LIKE 'A' is TRUE but 'æ' LIKE 'Æ' is FALSE.) See more
To investigage
Charset equals? (Rails - SQLite)
String stored potentially dirty (carriage returns, ...)
Thanks to help of #gazler and #basgys I was able to track down the problem:
It is in fact an encoding problem caused by the Digest::Sha2#hexdigest function. It returns a string that is encoded as ASCII-8BIT. When storing this in the database it seems to be converted automatically to a UTF-8 string (I check that by running a select hex(songhash) from songs query). However when using the string in the query, it does not seem to do that conversion.
Internally ruby seems to handle the different encoding conversions automatically. That is why "abc"=="abc" although they may have different encoding.
I'm sure that this is not expected behavior, however I don't know if it is a bug - and if it is a bug whether it is somewhere within ActiveRecord, the SQLite Driver, or SQLite itself.
My solution is now to append a .encode("UTF-8") to the result of the digest function.

PostgreSql + Query Statement having \r in between the attributes !

Suppose we have a textarea in which we put example string. Textbox contain :
Earth is revolving around the Sun.
But at the time of saving, I just pressed a enter key after "the sun". Now the statements in texbox ::
Earth is revolving around
the Sun
Now, in database where enter was pressed the \r is stored. Now i am trying to fetch the data but unable, because my query is of such type ::
SELECT * FROM "volume_factors" WHERE lower(volume_notes) like E'atest\\r\\n 100'
Actual data stored in database field
atest\r
100
Please suggest me to solve the issue.I have tried gsub to replace but no change.
search_text_array[1] = search_text_array[1].gsub('\\r\\n','\r\n')
Thanks in Advance !!!
Try this:
update volume_factors set volume_notes = regexp_replace(volume_notes, '\r\n', ' ');
That's to replace crlf with one space for data that is already in the database. You use postgresql's psql to do this.
To prevent new data containing crlf entering database, you should do it in the application. If you use ruby's gsub, do not use single quote, use double quote to recognize \n like this:
thestring.gsub("\n", " ")
Here we can replace \r\n by % to fetch the data.
Seaching Query will be like this ::
SELECT * FROM "volume_factors" WHERE lower(volume_notes) like E'atest% 100'
gsub function ::
search_text_array[1] = search_text_array[1].gsub('\\r\\n','%')

Resources